/* ========================================================================
 * zui-datagrid.js  v1.3 beta2
 * ======================================================================== */
+function ($) {
    'use strict';

    // DATAGRID CLASS DEFINITION
    // ======================

    var Datagrid = function (element, options) {
        this.$element = $(element)
        this.options = options
        this.tools = this.TOOLS()

        this.datanames = {
            tbody: 'zui.datagrid.tbody.dom',
            td_html: 'zui.datagrid.td.html',
            changeData: 'zui.datagrid.tr.changeData'
        }

        this.classnames = {
            s_checkbox: 'datagrid-checkbox',
            s_linenumber: 'datagrid-linenumber',
            s_edit: 'datagrid-column-edit',
            s_lock: 'datagrid-lock',
            s_menu: 'datagrid-menu-box',
            s_showhide: 'datagrid-showhide-box',
            th_cell: 'datagrid-cell',
            th_menu: 'datagrid-column-menu',
            btn_menu: 'datagrid-column-menu-btn',
            th_col: 'datagrid-col',
            th_field: 'datagrid-col-field',
            th_sort: 'datagrid-sortable',
            th_resize: 'datagrid-resize-head',
            th_resizemark: 'datagrid-column-resizemark',
            tr_child: 'datagrid-child-tr',
            tr_add: 'datagrid-add-tr',
            tr_selected: 'datagrid-selected-tr',
            td_edit: 'datagrid-edit-td',
            td_child: 'datagrid-child-td',
            td_linenumber: 'datagrid-linenumber-td',
            td_checkbox: 'datagrid-checkbox-td',
            li_asc: 'datagrid-li-asc',
            li_desc: 'datagrid-li-desc',
            li_showhide: 'datagrid-li-showhide',
            li_lock: 'datagrid-li-lock',
            li_unlock: 'datagrid-li-unlock'
        }
    }

    Datagrid.DEFAULTS = {
    	id: 'id',  //主键
        gridTitle: '',
        checkAction: '',
        columns: null, // Thead column module
        data: null, // Data source
        dataUrl: null, // Request data source URL, for processing (sorting / paging) results
        urlParms: null,                         //url带参数
        postData: null, // Send other data to server
        loadType: 'POST', // Ajax load request type
        dataType: 'json', // Data type of data source
        dataFrom: 'remote', // Optional 'local' | 'remote'
        fieldSortable: true, // Click the field to sort
        sortAll: true, // Sort scope, false = this page, true = all
        sortMode: {},
        selectMult: false, // When clicked tr, multiple selected (Default need to press Ctrl or Shift or used checkbox)
        linenumberAll: true, // All data together numbers
        showLinenumber: true, // Display linenumber column, Optional 'true' | 'false' | 'lock', (Optional 'true, false' is a boolean)
        showCheckboxcol: false, // Display checkbox column, Optional 'true' | 'false' | 'lock', (Optional 'true, false' is a boolean)
        showChildcol: undefined, // Display child button column, Optional 'true' | 'false' | 'lock', (Optional 'true, false' is a boolean). If not set false and 'hasChild == true', showChildcol = true
        showEditbtnscol: false, // Display edit buttons column, Optional 'true' | 'false' | 'edit', (Optional 'true, false' is a boolean, Optional 'edit' is custom column label)
        showTfoot: false, // Display the tfoot, Optional 'true' | 'false' | 'lock', (Optional 'true, false' is a boolean)
        showToolbar: false, // Display datagrid toolbar
        showNoDataTip: false, // Display 'no data' tips, Optional 'true' | 'false' | 'string', (Optional 'true, false' is a boolean, Optional 'string' is custom tips)
        toolbarItem: '', // Displayed on the toolbar elements, Optional 'all, add, edit, del, import, export, |'
        toolbarCustom: '', // Html code || function || jQuery dom object, custom elements, displayed on the toolbar
        columnResize: true, // Allow adjust the column width
        columnMenu: true, // Display the menu button on the column
        columnShowhide: true, // On the menu display (show / hide columns)
        columnLock: true, // On the menu display (lock / unlock columns)
        paging: true, // Display pagination component
        pagingAlign: 'center', // The pagination component alignment
        hasChild: false, // It contains child data, Optional (true | false)
        childOptions: {// Child grid options
            width: '100%',
            height: 'auto',
            paging: false,
            columnMenu: false
        },
        editMode: 'dialog', //编辑模式，dialog|navtab
        editUrl: null, // An action URL, for processing edit | add
        editCallback: null, // Callback for save
        editEnabled: true, // Editing mode, Optional 'false' | 'inline' | 'dialog', (Optional 'false' is a boolean)
        delUrl: null, // The delete URL, return delete tr results (json)
        delType: 'POST', // Delete URL of ajax request method
        dataPK: null, // Ajax delete|edit request to send only the primary key
        delConfirm: true, // Delete confirmation message, Optional 'true' | 'false' | 'message', (Optional 'true, false' is a boolean)
        delCallback: null, // Callback for delete
        jsonPrefix: '', // JSON object key prefix, for post data
        contextMenuH: true, // Right-click on the thead, display the context menu
        contextMenuB: true, // Right-click on the tbody tr, display the context menu
        fullGrid: false, // If the table width below gridbox width, stretching table width
        importOption: null, // Import btn options
        exportOption: null, // Export btn options
        beforeEdit: null, // Function - before edit method, return true execute edit method
        beforeDelete: null, // Function - before delete method, return true execute delete method
        showPageSelect: false
    };

    Datagrid.renderItem = function (value, data, items) {
        if (!items || !items.length) return ''
        var label = ''

        $.each(items, function(i, n) {
            if (typeof n[value] !== 'undefined') {
                label = n[value]
                return false
            }
        })

        return label
    }

    Datagrid.renderItemMulti = function(value, data, items) {
        if (!items || !items.length) return ''
        var label = [], val = $.type(value) === 'array' ? value : String(value).split(',')

        $.each(items, function (i, n) {
            $.each(val, function (k, v) {
                if (typeof n[v] !== 'undefined') {
                    label.push(n[v])
                    return false
                }
            })
        })

        return label.join(',')
    }

    Datagrid.prototype.TOOLS = function () {
        var that = this, options = that.options;
        var tools = {
            _getTotalInfo : function(column, data) {
                try{
                    if (!column.totalSummary) return null;
                    var sum = 0, count = 0, avg = 0, min = 0, max = 0;
                    if (data && data.length)
                    {
                        max = parseFloat(data[0][column.name]);
                        min = parseFloat(data[0][column.name]);
                        for (var i = 0; i < data.length; i++)
                        {
                            count += 1;
                            var value = data[i][column.name];
                            if (typeof (value) === "string") value = value.replace(/\$|\,/g, '');
                            value = parseFloat(value);
                            if (!value) continue;
                            if(column.totalSummary.sumFunc) {
                                //有自定义的合计算法
                                var customSum = column.totalSummary.sumFunc;
                                if (typeof customSum === 'string') customSum = customSum.toFunc()
                                if (typeof customSum === 'function') {
                                    return customSum.call(that, $trs, datas)
                                }
                            } else {
                                sum += value;
                            }
                            if (value > max) max = value;
                            if (value < min) min = value;
                        }
                        avg = sum * 1.0 / data.length;
                    }
                    return {
                        sum: sum,
                        count: count,
                        avg: avg,
                        min: min,
                        max: max
                    };
                } catch (e)
                {
                    return {};
                }
            },
            _getTotalCellContent: function (column, data) {
                var totalsummaryArr = [];
                if (column.totalSummary)
                {
                    var isExist = function (type)
                    {
                        for (var i = 0; i < types.length; i++)
                            if (types[i].toLowerCase() == type.toLowerCase()) return true;
                        return false;
                    };
                    var info = tools._getTotalInfo(column, data);
                    if (column.totalSummary.render)
                    {
                        var renderhtml = column.totalSummary.render(info, column, that.paging);
                        totalsummaryArr.push(renderhtml);
                    }
                    else if (column.totalSummary.type && info)
                    {
                        var types = column.totalSummary.type.split(',');
                        if (isExist('sum'))
                            totalsummaryArr.push("<div>Sum=" + info.sum.toFixed(2) + "</div>");
                        if (isExist('tsum'))
                            totalsummaryArr.push("<div>" + sum.toFixed(0) + "</div>");
                        if (isExist('count'))
                            totalsummaryArr.push("<div>Count=" + info.count + "</div>");
                        if (isExist('max'))
                            totalsummaryArr.push("<div>Max=" + info.max.toFixed(2) + "</div>");
                        if (isExist('min'))
                            totalsummaryArr.push("<div>Min=" + info.min.toFixed(2) + "</div>");
                        if (isExist('avg'))
                            totalsummaryArr.push("<div>Avg=" + info.avg.toFixed(2) + "</div>");
                    }
                }
                return totalsummaryArr.join('');
            },
            _getTotalSummaryHtml: function (data, classCssName, frozen)
            {
                var totalsummaryArr = [];
                if (classCssName)
                    totalsummaryArr.push('<tr class="l-grid-totalsummary ' + classCssName + '">');
                else
                    totalsummaryArr.push('<tr class="l-grid-totalsummary">');
                var hasTotalSummary = false;
                $(options.columns).each(function (columnindex, column)
                {
                    if (this.frozen != frozen) return;
                    if(column.totalSummary) hasTotalSummary = true;
                    //如果是行序号(系统列)
                    if (this.isrownumber)
                    {
                        totalsummaryArr.push('<td class="l-grid-totalsummary-cell l-grid-totalsummary-cell-rownumbers" style="width:' + this.width + 'px"><div>&nbsp;</div></td>');
                        return;
                    }
                    //如果是复选框(系统列)
                    if (this.ischeckbox)
                    {
                        totalsummaryArr.push('<td class="l-grid-totalsummary-cell l-grid-totalsummary-cell-checkbox" style="width:' + this.width + 'px"><div>&nbsp;</div></td>');
                        return;
                    }
                        //如果是明细列(系统列)
                    else if (this.isdetail)
                    {
                        totalsummaryArr.push('<td class="l-grid-totalsummary-cell l-grid-totalsummary-cell-detail" style="width:' + this.width + 'px"><div>&nbsp;</div></td>');
                        return;
                    }
                    totalsummaryArr.push('<td class="l-grid-totalsummary-cell');
                    if (this.islast)
                        totalsummaryArr.push(" l-grid-totalsummary-cell-last");
                    totalsummaryArr.push('" ');
                    totalsummaryArr.push('id="' + that.id + "|total" + that.totalNumber + "|" + column.__id + '" ');
                    totalsummaryArr.push('width="' + this._width + '" ');
                    if (column.name)
                    {
                        totalsummaryArr.push('columnname="' + column.name + '" ');
                    }
                    totalsummaryArr.push('columnindex="' + column.index + '" ');
                    totalsummaryArr.push('><div class="l-grid-totalsummary-cell-inner"');
                    if (column.align)
                        totalsummaryArr.push(' style="text-Align:' + column.align + ';"');
                    totalsummaryArr.push('>');
                    totalsummaryArr.push(tools._getTotalCellContent(column, data));
                    totalsummaryArr.push('</div></td>');
                });
                totalsummaryArr.push('</tr>');
                if (!frozen) that.totalNumber++;
                if(!hasTotalSummary) return '';
                return totalsummaryArr.join('');
            },
            getPageCount: function (pageSize, total) {
                return Math.ceil(total / pageSize)
            },
            getPageInterval: function (count, pageCurrent, showPageNum) {
                var half = Math.ceil(showPageNum / 2), limit = count - showPageNum,
                    start = pageCurrent > half ? Math.max(Math.min(pageCurrent - half, limit), 0) : 0,
                    end = pageCurrent > half ? Math.min((pageCurrent + half), count) : Math.min(showPageNum, count)

                if (end - start == showPageNum) end = end + 1
                if (end < showPageNum) end = end + 1
                if (start + 1 == end) end = end + 1

                return {start:start + 1, end:end}
            },
            getRight: function($obj) {
                var width = 0, index = $obj.data('index'), $firstTds = that.$tbody.find('> tr:first > td:lt('+ (index + 1) +')')

                if (!$firstTds || !$firstTds.length) $firstTds = that.$colgroupH.find('> col:lt('+ (index + 1) +')')
                $firstTds.each(function () {
                    var $td = $(this), w = $td.is(':hidden') ? 0 : $td.outerWidth()

                    width += w
                })

                return width
            },
            getRight4Lock: function (index) {
                var width = 0, $td = that.$lockTbody.find('> tr:first > td:eq(' + index + ')'), $firstTds = $td && $td.prevAll().add($td)

                if (!$firstTds || !$firstTds.length) $firstTds = that.$lockColgroupH.filter(':lt('+ (index + 1) +')')
                $firstTds.each(function () {
                    var $td = $(this), w = $td.is(':hidden') ? 0 : $td.outerWidth()

                    width += w
                })

                return width
            },
            beforeEdit: function ($trs, datas) {
                var beforeEdit = options.beforeEdit

                if (beforeEdit) {
                    if (typeof beforeEdit === 'string') beforeEdit = beforeEdit.toFunc()
                    if (typeof beforeEdit === 'function') {
                        return beforeEdit.call(that, $trs, datas)
                    }
                }

                return true
            },
            afterDelete: function() {
                var afterDelete = options.afterDelete

                if (afterDelete) {
                    if (typeof afterDelete === 'string') afterDelete = afterDelete.toFunc()
                    if (typeof afterDelete === 'function') {
                        afterDelete.call(that)
                    }
                }
            },
            // Correct colspan
            setColspan: function (column, colspanNum) {
                if (column.colspan) column.colspan = column.colspan + colspanNum - 1;
                column.index = column.index + colspanNum - 1;
                if (column.parent) this.setColspan(column.parent, colspanNum);
            },
            // set columns options
            setOp: function (op) {
                if (!op.name) {
                    op.menu = op.lock = op.edit = op.add = op.quickSort = false;
                } else {
                    op.menu = (typeof op.menu === 'undefined') ? true : op.menu;
                    op.lock = (typeof op.lock === 'undefined') ? true : op.lock;
                    op.edit = (typeof op.edit === 'undefined') ? true : op.edit;
                    op.add = (typeof op.add === 'undefined') ? true : op.add;
                    op.quicksort = (typeof op.quicksort === 'undefined') ? true : op.quicksort;
                }
                op.hide = (typeof op.hide === 'undefined') ? false : op.hide;

                return op;
            },
            // create trs by data source
            createTrsByData: function (data, refreshFlag) {
                var list = [];

                if (!that.$tbody) that.$tbody = $('<tbody></tbody>');
                if (data) {
                    if (data[$.zui.ajaxReturn.root]) list = data[$.zui.ajaxReturn.root];
                    if(data.hasOwnProperty('paging')) {
                        that.paging = $.extend(that.paging, data.paging);
                    } else if (data.hasOwnProperty($.zui.ajaxReturn.total)) {
                        that.paging.total = data[$.zui.ajaxReturn.total];
                    } else {
                        that.paging.total = list.length || 0;
                    }
                    //处理URL参数
                    if(!options.urlParms) {
                        options.urlParms = [];
                    }
                    options.urlParms[$.zui.ajaxReturn.total] = that.paging.total;
                    if(data.hasOwnProperty('Sum')) {
                        that.paging.Sum = data.Sum;
                        //options.urlParms.Sum = JSON.stringify(data.Sum);
                    } else {
                        that.paging.Sum = {};
                    }

                    if (!that.paging.total)
                        list = [];

                    if (typeof data === 'object' && that.paging.total) {
                        if (data[$.zui.pageInfo.pageSize]) {
                            if (refreshFlag && that.paging.pageSize != data[$.zui.pageInfo.pageSize]) {
                                that.$boxP && that.$boxP.trigger('zui.datagrid.paging.pageSize', data[$.zui.pageInfo.pageSize])
                            }
                            that.paging.pageSize = parseInt(data[$.zui.pageInfo.pageSize], 10)
                        }
                    }

                    that.paging.pageCount = tools.getPageCount(that.paging.pageSize, that.paging.total)

                    if (that.paging.pageCurrent > that.paging.pageCount) that.paging.pageCurrent = that.paging.pageCount
                    if (!that.paging.pageCurrent) that.paging.pageCurrent = 1

                    this.initTbody(list, refreshFlag)
                }

                if (!that.init_tbody) that.$tbody.appendTo(that.$tableB)
                if (!that.init_thead) that.initThead()
            },
            // initTbody
            initTbody: function (data, refreshFlag) {
                var tools = this, allData = that.allData, type = options.dataType || 'json', model = that.columnModel, hiddenFields = that.hiddenFields, regional = that.regional, newData = [], attach = that.attach, json
                var paging = that.paging, start = 0, end = paging.pageSize
                var doInit = function () {
                    type = type.toLowerCase()
                    if (data) allData = that.allData = data
                    if (options.dataFrom === 'local') that.$element.data('allData', data)
                    if (!allData.length) {
                        end = 0
                    } else {
                        if (options.dataFrom === 'local') {
                            start = (paging.pageSize * (paging.pageCurrent - 1))
                            end = start + paging.pageSize
                            if (paging.total != allData.length) paging.total = allData.length
                            if (start > allData.length) start = paging.pageSize * (paging.pageCount - 1)
                        } else {
                            if (allData.length > paging.pageSize) end = paging.pageSize
                        }
                    }
                    if (end > allData.length) end = allData.length
                    that.initPaging();        //更新一下分页条
                    // array to json
                    if (type === 'array' && data && data.length && $.type(data[0]) === 'array') {
                        var a1 = start, a2 = end, arrData = [], _index

                        if (options.dataFrom === 'local') {
                            a1 = 0
                            a2 = allData.length
                        }
                        for (var i = a1; i < a2; i++) {
                            json = {}
                            _index = 0
                            $.each(allData[i], function (k, v) {
                                var obj, val = v

                                if (model[_index] && model[_index].gridNumber)   _index ++
                                if (model[_index] && model[_index].gridCheckbox) _index ++
                                if (typeof val === 'string') val = '"'+ val +'"'

                                if (model[_index] && !model[_index].gridEdit) {
                                    obj = '{"' + model[_index].name + '":' + val + '}'
                                    $.extend(json, JSON.parse(obj))
                                } else { // init hidden fields
                                    if (model[_index] && model[_index].gridEdit) _index ++
                                    if (_index >= model.length && hiddenFields) {
                                        if (hiddenFields[k - model.length]) {
                                            obj = '{"' + hiddenFields[k - model.length] + '":' + val + '}'
                                            $.extend(json, JSON.parse(obj))
                                        }
                                    }
                                }
                                _index++
                            })

                            arrData.push(json)
                        }

                        allData = that.allData = arrData
                    }

                    // create cuttent page data
                    for (var i = start; i < end; i++) {
                        json = $.extend({}, that.allData[i], attach)
                        newData.push(json)
                    }

                    tools.createTrs(newData, refreshFlag)

                    that.data = newData

                    that.$element.trigger('afterLoad.zui.datagrid', {datas: newData})
                }

                if (refreshFlag && that.$boxM) {
                    that.$boxM.show().trigger('zui.ajaxStop').trigger('zui.ajaxStart', [50, doInit])
                } else {
                    doInit()
                }
            },
            // create tbody - tr
            createTrs: function (datas, refreshFlag) {
                var tools = this, model = that.columnModel, paging = that.paging, trs = [], editFrag = $.zui.doRegional(FRAG.gridEditBtn, that.regional), childFrag = $.zui.doRegional(FRAG.gridExpandBtn, that.regional), lockedCols = [], isRenderOnly = false

                if (refreshFlag) {
                    // remebered lock columns
                    $.each(model, function (i, n) {
                        if (n.locked) {
                            that.colLock(n.th, false)
                            n.lock_refresh = true
                        }
                    })

                    that.$tbody.empty()
                    that.$lockTableH && that.$lockTableH.empty()
                    that.$lockTableB && that.$lockTableB.empty()
                }

                if (!datas.length && !refreshFlag) {
                    var emptyObj = {}

                    emptyObj[model[0].name] = '0'
                    isRenderOnly = true
                    datas = []
                    datas.push(emptyObj)
                }

                for (var i = 0, l = datas.length; i < l; i++) {
                    var trData = datas[i], modellength = model.length, linenumber = options.linenumberAll ? ((paging.pageCurrent - 1) * paging.pageSize + (i + 1)) : (i + 1),
                        tds = [], n, tdHtml = $.zui.StrBuilder(), $td, name, label, _label, render_label, align, cls, display

                    $.extend(trData, {gridNumber: linenumber, gridIndex: i})

                    for (var j = 0; j < modellength; j++) {
                        n = model[j]
                        name = n.name || 'datagrid-noname'
                        label = trData[name]
                        align = ''
                        cls = []
                        _label = ''
                        display = ''
                        render_label = ''

                        if (typeof label === 'undefined' || label === 'null' || label === null) label = ''
                        _label = label

                        if (n.align) align = ' align="'+ n.align +'"'
                        if (n.gridChild) label = childFrag
                        if (n.gridCheckbox) label = '<input type="checkbox" data-toggle="icheck" name="datagrid.checkbox" value="true">'
                        if (n.gridEdit) {
                            label = editFrag
                            cls.push(that.classnames.s_edit)
                        }

                        /* for tfoot */
                        if (n.calc) {
                            if (!n.calc_count) n.calc_count = datas.length

                            var number = label ? (String(label).isNumber() ? Number(label) : 0) : 0

                            if (n.calc === 'sum' || n.calc === 'avg') n.calc_sum = (n.calc_sum || 0) + number
                            else if (n.calc === 'max') n.calc_max = n.calc_max ? (n.calc_max < number ? number : n.calc_max) : number
                            else if (n.calc === 'min') n.calc_min = n.calc_min ? (n.calc_min > number ? number : n.calc_min) : number
                        }

                        if (n.gridChild)    cls.push(that.classnames.td_child)
                        if (n.gridNumber)   cls.push(that.classnames.td_linenumber)
                        if (n.gridCheckbox) cls.push(that.classnames.td_checkbox)

                        if (cls.length) cls = ' class="'+ cls.join(' ') +'"'
                        else cls = ''

                        if (refreshFlag && n.hidden) display = ' style="display:none;"'

                        /* render */
                        if (n.items && !n.render) {
                            if (n.attrs && n.attrs['multiple'])
                                n.render = $.datagrid.renderItemMulti
                            else
                                n.render = $.datagrid.renderItem
                        }
                        if (n.render && typeof n.render === 'string') n.render = n.render.toFunc()
                        if (n.render && typeof n.render === 'function') {
                            if (n.items) {
                                if (typeof n.items === 'string') {
                                    if (n.items.trim().startsWith('[')) n.items = n.items.toObj()
                                    else n.items = n.items.toFunc()
                                }

                                if (!that.renderTds) that.renderTds = []

                                var delayRender = function (index, label, trData, n) {
                                    $.when(n.items.call(that)).done(function (item) {
                                        n.items = item

                                        that.delayRender--
                                    })
                                }

                                if (typeof n.items === 'function') {
                                    if (!i) {
                                        if (!that.delayRender) that.delayRender = 0
                                        that.delayRender++
                                        delayRender((i * j), _label, trData, n)
                                    }
                                    that.renderTds.push({trindex: i, tdindex: j, label: _label, data: trData, render: n.render})
                                } else {
                                    render_label = n.render.call(that, _label, trData, n.items)
                                }
                            } else {
                                render_label = n.render.call(that, _label, trData)
                            }
                        }

                        tdHtml
                            .add('<td')
                            .add(align)
                            .add(cls)
                            .add(display)
                            .add('><div>')
                            .add(render_label || label)
                            .add('</div></td>')

                        tds.push(tdHtml.toString())
                    }

                    trs.push('<tr>' + tds.join('') + '</tr>')

                    if (isRenderOnly) {
                        trs = []
                        trs.push(tools.createNoDataTr(true) || '')
                    } else {
                        trs.push(tools.createChildTr(null, trData))
                    }
                }
                if(datas.length>1) {
                    trs.push(tools._getTotalSummaryHtml(datas));
                }

                that.$tbody.html(trs.join(''))

                if (refreshFlag) {
                    that.initEvents()
                    if (options.editEnabled) that.showActionButton()

                    if (!datas.length)
                        tools.createNoDataTr()

                    that.$boxP && that.$boxP.trigger('zui.datagrid.paging.jump')

                    // locked
                    $.each(model, function (i, n) {
                        if (n.lock_refresh) {
                            that.colLock(n.th, true)
                            delete n.lock_refresh
                        }
                    })

                    setTimeout(function () {
                        that.$tableH.initui()
                        that.$tableB.initui()
                        that.$lockTableB && that.$lockTableB.initui()

                        that.fixedHeight()

                        that.$boxM && that.$boxM.trigger('zui.ajaxStop').hide()
                    }, datas.length + 1)
                }
            },
            replacePlh: function (url, data) {
                return url.replace(/{\/?[^}]*}/g, function ($1) {
                    var key = $1.replace(/[{}]+/g, ''), val = data[key]

                    if (typeof val === 'undefined' || val === 'null' || val === null)
                        val = ''

                    return val
                })
            },
            createNoDataTr: function (str) {
                if (that.options.showNoDataTip) {
                    if (str) {
                        return '<tr class="datagrid-nodata"><td colspan="' + that.columnModel.length + '">' + (typeof that.options.showNoDataTip === 'string' ? that.options.showNoDataTip : $.zui.getRegional('datagrid.noData')) + '</td></tr>'
                    } else if (!that.$tbody.find('> tr').length) {
                        $('<tr class="datagrid-nodata"></tr>').html('<td colspan="' + that.columnModel.length + '">' + (typeof that.options.showNoDataTip === 'string' ? that.options.showNoDataTip : $.zui.getRegional('datagrid.noData')) + '</td>').appendTo(that.$tbody)
                    }
                }
            },
            createChildTr: function ($tr, trData) {
                if ($tr && $tr.next() && $tr.next().hasClass(that.classnames.tr_child))
                    return

                if (options.hasChild && options.childOptions && options.childOptions.dataUrl) {
                    that.childOptions = $.extend(true, {}, Datagrid.DEFAULTS, options.childOptions)

                    var child = '<tr class="' + that.classnames.tr_child + '"><td colspan="' + that.columnModel.length + '" style="width:100%; padding:10px; background:#FAFAFA;"><table class="table-child"></table></td></tr>'

                    if ($tr && $tr.length)
                        $tr.after(child)
                    else
                        return child
                }
            },
            // Parameters can be (jQuery Object || number)
            getNoChildTrIndex: function (row) {
                var index

                if (isNaN(row)) {
                    if (!row || !row.length) return -1
                    index = row.index()
                } else {
                    index = parseInt(row, 10)
                }
                if (that.options.hasChild && that.options.childOptions)
                    index = index * 2

                return index
            },
            // Parameters can be (jQuery Object || number)
            getNoChildDataIndex: function (row) {
                var data_index

                if (isNaN(row))
                    data_index = this.getNoChildTrIndex(row)
                else
                    data_index = parseInt(row, 10)

                if (data_index === -1) return data_index
                if (that.options.hasChild && that.options.childOptions)
                    data_index = data_index / 2

                return data_index
            },
            // ajax load data by url
            loadData: function (data, refreshFlag) {
                var tools = this, url = options.dataUrl, dataType = options.dataType || 'json', model = that.columnModel;
                that.$boxB.scrollTop(0).scrollLeft(0);
                that.$boxM && that.$boxM.show().trigger('zui.ajaxStart')

                dataType = dataType.toLowerCase()
                if (dataType === 'array') dataType = 'text'

                if (options.postData) {
                    if (typeof options.postData === 'string') {
                        if (options.postData.trim().startsWith('{')) options.postData = options.postData.toObj()
                        else options.postData = options.postData.toFunc()
                    }

                    var queryData = options.postData;
                    if (typeof options.postData === 'function') {
                        queryData = options.postData.apply()
                    }
                    if (typeof queryData === 'object') {
                        if (!data) data = queryData
                        else data = $.extend({}, queryData, data)
                    }
                }
                if(options.sortMode) {
                    if(!data) {
                        data = {};
                    }
                    data[$.zui.pageInfo.orderField] = options.sortMode.name;
                    data[$.zui.pageInfo.orderDirection] = options.sortMode.direction;
                }
                data[$.zui.pageInfo.pageSize] = that.paging.pageSize;
                data[$.zui.pageInfo.pageVar] = that.paging.pageCurrent;

                var urlParms = $.isFunction(options.urlParms) ? options.urlParms.call(that) : options.urlParms;
                if (urlParms) {
                    for (name in urlParms) {
                        url += (url.indexOf('?') === -1) ? "?" : "&";
                        url += name + "=" + urlParms[name];
                    }
                }

                $.zui.ajax('doajax', {
                    url: url,
                    data: data || {},
                    type: options.loadType,
                    cache: options.cache || false,
                    dataType: dataType,
                    loadingmask: false,
                    okCallback: function (response) {
                        if (dataType === 'json') {
                            tools.createTrsByData(response, refreshFlag)
                        } else if (dataType === 'text') {
                            if ($.type(response) !== 'array')
                                response = []

                            tools.createTrsByData(response, refreshFlag)
                        } else if (dataType === 'xml') {
                            var xmlData = [], obj

                            $(response).find('row').each(function () {
                                obj = {}

                                $(this).find('cell').each(function (i) {
                                    var $cell = $(this), label = $cell.text(), name = $cell.attr('name')

                                    obj[name] = label
                                })

                                xmlData.push(obj)
                            })

                            if (xmlData.length) tools.createTrsByData(xmlData, refreshFlag)
                        } else {
                            $.zui.debug('Datagrid Plugin: The options \'dataType\' is incorrect!')
                        }
                        if (options.showCheckboxcol) {
                            var ckbox = that.$boxH.find('input[type="checkbox"]');
                            if(ckbox.is(':checked')){
                                ckbox.attr('checked', false).iCheck('update');
                            }
                        }
                    },
                    errCallback: function () {
                        that.$boxM.trigger('zui.ajaxStop');
                    },
                    failCallback: function () {
                        that.destroy()
                    }
                })
            },
            // append columns
            appendColumns: function () {
                that.childColumn = {name: 'gridChild', gridChild: true, width: 30, minWidth: 30, label: '...', align: 'center', menu: false, edit: false, quicksort: false, totalSummary: null}
                that.linenumberColumn = {name: 'gridNumber', gridNumber: true, width: 30, minWidth: 30, label: 'No.', align: 'center', menu: false, edit: false, quicksort: false}
                that.checkboxColumn = {name: 'gridCheckbox', gridCheckbox: true, width: 30, minWidth: 30, label: 'Checkbox', align: 'center', menu: false, edit: false, quicksort: false}
                that.editBtnsColumn = {name: 'gridEdit', gridEdit: true, width: 110, minWidth: 110, label: (typeof options.showEditbtnscol === 'string' ? options.showEditbtnscol : ''), align: 'center', menu: false, edit: false, hide: false, quicksort: false}
            },
            // setBoxb - height
            setBoxbH: function (height) {
                var boxH = height || that.boxH || options.height, topM = 0, h
                if (boxH < 100) return
                if (isNaN(boxH)) {
                    if (boxH === 'auto') {
                        var minBoxH = (that.$boxT ? that.$boxT.outerHeight() : 0)
                            + (that.$boxH ? that.$boxH.outerHeight() : 0)
                            + (that.$toolbar ? that.$toolbar.outerHeight() : 0)
                            + (that.$boxP ? that.$boxP.outerHeight() : 0)
                            //+ (that.$boxB ? that.$boxB.outerHeight() : 0)
                            + (that.$boxF ? that.$boxF.outerHeight() : 0)
                            + 1

                        //TODO 还需要跟踪窗口的缩放事件
                        boxH = $(window).height() - $('header').outerHeight() - $('footer').outerHeight() - 4
                        boxH = $(window).height() - that.$grid.offset().top - $('footer').outerHeight() - 4;
                        if (boxH < minBoxH + 66) {
                            $(window).height(minBoxH + that.$grid.offset().top + $('footer').outerHeight() + 70);
                            boxH = minBoxH + 66;
                        }
                        that.$grid.height(boxH)//.css({'overflow':'hidden'})    //为了适应chosen，所以不能自动隐藏
                        that.boxH = boxH
                    } else {
                        boxH = that.$grid.height()
                    }
                }

                if (that.$boxT) {
                    h = that.$boxT.outerHeight()
                    boxH -= h
                    topM += h
                }
                if (that.$toolbar) {
                    h = that.$toolbar.outerHeight()
                    boxH -= h
                    topM += h
                }
                if (that.$boxP)
                    boxH -= that.$boxP.outerHeight()
                if (that.$boxF)
                    boxH -= that.$boxF.outerHeight()

                topM += that.$tableH.outerHeight()>1?that.$tableH.outerHeight():33
                boxH -= that.$boxH.outerHeight()>1?that.$boxH.outerHeight():33
                boxH--;
                if (boxH < 0) boxH = 0

                that.$boxB.height(boxH)
                that.$boxM.height(boxH).css({top: topM})
                that.$lockB && that.$lockB.height(boxH)

                if (that.$element.data('zui.datagrid.parent'))
                    that.$element.data('zui.datagrid.parent').closest('table').datagrid('fixedHeight')
            },
            // column menu - toggle show submenu
            showSubMenu: function ($li, $menu, $submenu) {
                var left, width = $menu.outerWidth(), submenu_width = $submenu.data('width') || $submenu.outerWidth(), animate_op, boxWidth = that.$boxH.width()
                var hidesubmenu = function ($li, $menu, $submenu) {
                    left = $menu.offset().left - that.$grid.offset().left - 1
                    animate_op = {left: '50%'}

                    $li.removeClass('active')

                    if ($menu.hasClass('position-right') || (boxWidth - left < width + submenu_width)) {
                        $submenu.css({left: 'auto', right: '100%'})
                        animate_op = {right: '50%'}
                    } else {
                        $submenu.css({left: '100%', right: 'auto'})
                    }
                    animate_op.opacity = 0.2

                    $submenu.stop().animate(animate_op, 'fast', function () {
                        $(this).hide()
                    })
                }

                $li.hover(function () {
                    $submenu.appendTo($li)
                    left = $menu.offset().left - that.$grid.offset().left - 1
                    animate_op = {left: '100%'}

                    if ($menu.hasClass('position-right') || (boxWidth - left < width + submenu_width)) {
                        $submenu.css({left: 'auto', right: '50%'})
                        animate_op = {right: '100%'}
                    } else {
                        $submenu.css({left: '50%', right: 'auto'})
                    }
                    animate_op.opacity = 1

                    $li.addClass('active')
                    $submenu.show().stop().animate(animate_op, 'fast')
                }, function () {
                    hidesubmenu($li, $menu, $submenu)
                })

                $li.on('zui.datagrid.th.submenu.hide', function (e, menu, submenu) {
                    hidesubmenu($(this), menu, submenu)
                })
            },
            // column menu - lock/unlock
            locking: function ($th) {
                var index = $th.data('index'), columnModel = that.columnModel[index], lockFlag = columnModel.lock, locked = columnModel.locked, $menu = that.$menu, $ul = $menu.find('> ul'), $lockli = $ul.find('> li.' + that.classnames.li_lock), $unlockli = $lockli.next()

                if (locked) {
                    $lockli.addClass('disable')
                    $unlockli.removeClass('disable')
                } else {
                    $unlockli.addClass('disable')
                    $lockli.removeClass('disable')
                }

                if (lockFlag) {
                    $lockli.show().off('click').on('click', function () {
                        if ($lockli.hasClass('disable')) return

                        $menu.hide().data('zui.datagrid.menu.btn').removeClass('active')
                        that.colLock($th, true)
                    })

                    $unlockli.show().off('click').on('click', function () {
                        if ($unlockli.hasClass('disable')) return

                        $menu.hide().data('zui.datagrid.menu.btn').removeClass('active')
                        that.colLock($th, false)
                    })
                } else {
                    $lockli.hide().off('click')
                    $unlockli.hide().off('click')
                }
            },
            // create show/hide column panel
            createShowhide: function () {
                var $showhide

                if (!that.$showhide) {
                    that.col_showhide_count = that.columnModel.length
                    $showhide = $('<ul class="' + that.classnames.s_showhide + '" role="menu"></ul>')

                    $.each(that.columnModel, function (i, n) {
                        if (n.gridNumber || n.gridCheckbox || n.gridEdit) that.col_showhide_count --

                        var $col = $(FRAG.gridShowhide.replaceAll('#index#', n.index).replaceAll('#label#', (n.label || ''))).toggleClass('nodisable', !!(n.gridNumber || n.gridCheckbox || n.gridEdit))
                        var colClick = function(n) {
                            $col.click(function() {
                                if ($(this).hasClass('disable')) return false

                                var $this = $(this), check = !$this.find('i').hasClass('icon-checked'), index = n.index

                                $this.toggleClass('datagrid-col-check')
                                    .find('i').attr('class', 'icon icon' + (check ? '-checked' : '-check-empty'))

                                that.showhideColumn(n.th, check)

                                if (!(n.gridNumber || n.gridCheckbox || n.gridEdit)) {
                                    that.col_showhide_count = check ? that.col_showhide_count + 1 : that.col_showhide_count - 1
                                }

                                if (that.col_showhide_count == 1) $showhide.find('> li.datagrid-col-check').addClass('disable')
                                else $showhide.find('> li.disable').removeClass('disable')

                                $showhide.find('> li.nodisable').removeClass('disable')
                            })
                        }

                        colClick(n)
                        $col.appendTo($showhide)

                        if (n.hide) $col.trigger('click')
                    })

                    $showhide.appendTo(that.$grid)
                    $showhide.data('width', $showhide.outerWidth())
                    that.$showhide = $showhide
                }
            },
            // column - display/hide
            showhide: function (model, showFlag) {
                var index = model.index, $th = model.th, $trs = that.$tbody.find('> tr'), display = showFlag ? '' : 'none'
                var setColspan = function (column) {
                    var _colspan = column.colspan

                    if (showFlag) _colspan ++
                    else _colspan --

                    if (!_colspan) column.th.css('display', 'none')
                    else column.th.css('display', '')

                    column.th.attr('colspan', _colspan)
                    column.colspan = _colspan

                    if (column.parent) setColspan(column.parent)
                }

                if (typeof model.hidden === 'undefined') model.hidden = false
                if (model.hidden === !showFlag) return

                model.hidden = !showFlag

                $th.css('display', display)
                $trs.find('> td:eq(' + index + ')').css('display', display)
                that.$colgroupH.find('> col').eq(index).css('display', display)
                that.$colgroupB.find('> col').eq(index).css('display', display)
                if (that.$boxF) {
                    that.$tableF.find('> thead > tr > th:eq(' + index + ')').css('display', display)
                    that.$colgroupF.find('> col').eq(index).css('display', display)
                }

                if (model.calc) {
                    that.$tfoot && that.$tfoot.trigger('zui.datagrid.tfoot.resizeH')
                }

                if (model.parent) setColspan(model.parent)

                // fixed width && height for child datagrid
                that.$tbody.find('> tr.' + that.classnames.tr_child + ':visible > td table').trigger('zui.datagrid.child.resize')
            },
            // jump to page
            jumpPage: function (pageCurrent, pageSize) {
                var allData = that.allData, remoteDatas

                if (pageCurrent) {
                    that.paging.oldPageCurrent = that.paging.pageCurrent
                    that.paging.pageCurrent = pageCurrent
                }
                if (pageSize) {
                    if(that.paging.pageSize==pageSize){
                        return;
                    }
                    that.paging.pageSize = pageSize
                    that.paging.pageCount = this.getPageCount(pageSize, that.paging.total)

                    if (that.paging.pageCurrent > that.paging.pageCount)
                        that.paging.pageCurrent = that.paging.pageCount;
                }

                if (options.dataFrom === 'remote') {
                    this.loadData({}, true)
                } else {
                    this.initTbody(allData, true)
                }
            },
            // column - quicksort
            quickSort: function (model) {
                if (that.isDom) {
                    if (options.dataFrom === 'local') return
                    options.sortAll = true
                }

                var $th = model.th, data = that.data, allData = that.allData, postData, direction, name = model.name, type = model.type, $ths = that.$thead.find('> tr > th.datagrid-quicksort-th')

                if (!name) name = 'datagrid-noname'

                var sortData = function (data) {
                    data.sort(function (a, b) {
                        var typeA = (typeof a[name]), typeB = (typeof b[name])

                        if (type === 'boolean' || type === 'number' || (typeA = typeB === 'number') || (typeA = typeB === 'boolean')) {
                            return model.sortAsc ? (a[name] - b[name]) : (b[name] - a[name])
                        } else {
                            return model.sortAsc ? String(a[name]).localeCompare(b[name]) : String(b[name]).localeCompare(a[name])
                        }
                    })
                }

                $ths.find('> div > .datagrid-label > i').remove()
                if (model.sortAsc) {
                    direction = 'desc'
                    model.sortAsc = false
                } else {
                    direction = 'asc'
                    model.sortAsc = true
                }
                $th.find('> div > .datagrid-label').prepend('<i class="icon icon-long-arrow-' + (model.sortAsc ? 'up' : 'down') + '"></i>')

                if (options.sortAll) {
                    if (options.dataFrom === 'remote') {
                        that.options.sortMode = {name: name, direction: direction};
                        this.loadData(postData, true)
                    } else {
                        sortData(allData)
                        this.initTbody(allData, true)
                    }
                } else {
                    sortData(data)
                    this.createTrs(data, true)
                }
            },
            // set data for Dom
            setDomData: function (tr) {
                var columnModel = that.columnModel, data = {}, hideDatas = tr.attr('data-hidden-datas'), attach = that.attach

                tr.find('> td').each(function (i) {
                    var $td = $(this), model = columnModel[i], val = $td.attr('data-val') || $td.text()

                    if (!model.name) data['datagrid-noname'+ i] = val
                    else data[model.name] = val
                })

                if (hideDatas) hideDatas = hideDatas.toObj()
                $.extend(data, attach, {gridNumber: (tr.index() + 1)}, hideDatas)

                tr.data('initData', data)

                return data
            },
            contextmenuH: function () {
                var tools = this

                that.$tableH.on('contextmenu', 'tr', function (e) {
                    if (!that.$showhide) tools.createShowhide()

                    var posX = e.pageX
                    var posY = e.pageY

                    if ($(window).width()  < posX + that.$showhide.width())  posX -= that.$showhide.width()
                    if ($(window).height() < posY + that.$showhide.height()) posY -= that.$showhide.height()


                    that.$showhide
                        .appendTo('body')
                        .css({left: posX, top: posY, opacity: 1, 'z-index': 9999}).show()

                    $(document).on('click', function (e) {
                        var $showhide = $(e.target).closest('.' + that.classnames.s_showhide)
                        if (!$showhide.length)
                            that.$showhide.css({left: '50%', top: 0, opacity: 0.2, 'z-index': ''}).hide().appendTo(that.$grid)
                    })

                    e.preventDefault()
                    e.stopPropagation()
                })
            },
            contextmenuB: function ($tr, isLock) {
                if(!options.dataUrl && !options.editUrl && (options.dataFrom != 'remote' || !options.delUrl)) {
                    return;
                }
                $tr.contextmenu('show',
                    {
                        exclude: 'input, .bootstrap-select',
                        items: [
                            {
                                icon: 'refresh',
                                title: $.zui.getRegional('datagrid.refresh'),
                                skip: !options.dataUrl,
                                func: function (parent, menu) {
                                    that.refresh()
                                }
                            },
                            {
                                title: 'diver',
                                skip:!options.editUrl && (options.dataFrom != 'remote' || !options.delUrl)
                            },
                            {
                                icon: 'plus',
                                title: $.zui.getRegional('datagrid.add'),
                                skip: !options.editUrl,
                                func: function (parent, menu) {
                                    that.doAddRow()
                                }
                            },
                            {
                                icon: 'edit',
                                title: $.zui.getRegional('datagrid.edit'),
                                skip: !options.editUrl,
                                func: function (parent, menu) {
                                    var $tr = parent

                                    if (isLock)
                                        $tr = that.$tbody.find('> tr:eq(' + $tr.index() + ')')
                                    that.doEditRow($tr)
                                }
                            },
                            {
                                icon: 'remove',
                                title: $.zui.getRegional('datagrid.del'),
                                skip: options.dataFrom != 'remote' || !options.delUrl,
                                func: function (parent, menu) {
                                    var $tr = parent
                                        if (isLock) $tr = that.$tbody.find('> tr:eq('+ $tr.index() +')')
                                    that.delRows($tr)
                                }
                            }
                        ]
                    }
                )
            }
        }

        return tools
    }

    Datagrid.prototype.init = function () {
        if (!this.$element.isTag('table')) return
        if (this.$element.data('zui.datagrid.init')) return

        this.$element.data('zui.datagrid.init', true)

        var that = this, options = that.options, tools = that.tools, $parent = that.$element.parent(), gridHtml = $.zui.StrBuilder()

        that.$element.data('zui.datagrid.parent', $parent)
        //移除原表体中已经有的表脚描述
        that.$element.find('> tfoot').remove()

        //根据工具条的设置来确定是否需要显示工具条
        if(!options.showToolbar && (options.toolbarItem || options.toolbarCustom)) {
            options.showToolbar = true;
        }
        options.height = options.height || 'auto'
        that.isDom = false
        that.columnModel = []
        that.inputs = []
        that.regional = $.zui.regional.datagrid

        gridHtml
            .add('<div class="zui-datagrid">')
            .add(options.gridTitle ? '<div class="datagrid-title">' + options.gridTitle + '</div>' : '')
            .add(options.showToolbar ? '<div class="datagrid-toolbar"></div>' : '')
            .add('<div class="datagrid-box-h"><div class="datagrid-wrap-h"><table class="table table-bordered"><colgroup></colgroup></table></div></div>')
            .add('<div class="datagrid-box-b"><div class="datagrid-wrap-b"></div></div>')
            .add('<div class="datagrid-box-m"></div>')
            .add((options.paging||options.checkAction) ? '<div class="datagrid-paging-box"></div>' : '')
            .add('</div>')

        that.$grid = $(gridHtml.toString()).insertAfter(that.$element).css('height', options.height)
        that.$boxH = that.$grid.find('> div.datagrid-box-h')
        that.$boxB = that.$boxH.next()
        that.$boxM = that.$boxB.next().css('height', options.height)
        that.$boxP = (options.paging||options.checkAction) ? that.$boxM.next() : null
        that.$boxT = options.gridTitle ? that.$grid.find('> div.datagrid-title') : null
        that.$toolbar = options.showToolbar ? that.$boxH.prev() : null
        that.$tableH = that.$boxH.find('> div > table')
        that.$tableB = that.$element
        Do.ready('scrollbar', function () {
            //$('body').css({overflow:'hidden'});
            that.$boxB.perfectScrollbar();
        });

        if(!that.$boxT) {
            if(!that.$toolbar) {
                that.$boxH.css({'border-top': '0px'});
            } else {
                that.$toolbar.css({'border-top': '0px'});
            }
        } else {
            that.$boxT.css({'border-top': '0px'});
        }

        that.$grid.data('zui.datagrid.table', that.$element.clone())
        that.$boxB.find('> div').append(that.$element)

        that.initTop()

        if (typeof options.paging === 'string') options.paging = options.paging.toObj()
        that.paging = $.extend({}, {pageSize: 30, selectPageSize: '30,60,90', pageCurrent: 1, total: 0, showPagenum: 5}, (typeof options.paging === 'object') && options.paging)
        that.attach = {gridNumber: 0, gridCheckbox: '#checkbox#', gridEdit: '#edit#'}
        that.$thead = that.$element.find('> thead')
        that.$tbody = that.$element.find('> tbody')

        if (that.$tbody && that.$tbody.find('> tr').length) {
            that.isDom = true
            //转换的表格暂时不支持排序
            that.options.dataFrom = 'local'

            that.setColumnModel()

            that.$tbody.find('> tr > td').each(function () {
                var $td = $(this), html = $td.html()

                $td.html('<div>' + html + '</div>')
            })

            if (!that.paging.total) {
                that.paging.total = that.$tbody.find('> tr').length
                that.paging.pageCount = 1
            } else {
                that.paging.pageCount = tools.getPageCount(that.paging.pageSize, that.paging.total)
            }

            that.paging.pageCurrent = 1
            that.initThead()
        } else {
            that.$tbody = null
            that.$element.find('> tbody').remove()

            if (options.columns) {
                if (typeof options.columns === 'string') {
                    if (options.columns.trim().startsWith('[')) {
                        options.columns = options.columns.toObj()
                    } else {
                        options.columns = options.columns.toFunc()
                    }
                }
                if (typeof options.columns === 'function') {
                    options.columns = options.columns.call()
                }

                that.$thead = null
                that.$element.find('> thead').remove()
                that.createThead()
            } else {
                if (that.$thead && that.$thead.length && that.$thead.find('> tr').length) {
                    that.setColumnModel()
                } else {
                    $.zui.debug('Datagrid Plugin: No set options \'columns\' !')
                    that.destroy()
                    return
                }
            }

            if (options.data || options.dataUrl) {
                that.createTbody()
            } else {
                $.zui.debug('Datagrid Plugin: No options \'data\' or \'dataUrl\'!')
                that.destroy()
            }
        }
    }

    //是否包含汇总
    Datagrid.prototype.isTotalSummary = function () {
        for (var i = 0; i < this.columnModel.length; i++)
        {
            if (this.columnModel[i].totalSummary) return true;
        }
        return false;
    }

    // DOM to datagrid - setColumnModel
    Datagrid.prototype.setColumnModel = function () {
        var that = this, options = that.options, $trs = that.$thead.find('> tr'), rows = [], ths = [], trLen = $trs.length,
            processWidth = function(oW){
                if(!oW) {
                    return 'auto';
                } else if(oW.substr(-2).toLowerCase()=='px') {
                    return oW.substr(0, -2);
                } else if (oW.substr(-1)=='%') {
                    oW = parseInt(oW);
                    return oW*that.$element.parent().width()/100;
                } else if (parseInt(oW)) {
                    oW = parseInt(oW);
                    if(oW>100) {
                        return oW;
                    }
                    return oW*that.$element.parent().width()/100;
                } else {
                    return 'auto';
                }
            }

        if (!that.isDom) {
            that.tools.appendColumns()

            var $th, _rowspan = trLen > 1 ? ' rowspan="' + trLen + '"' : ''

            if (options.showCheckboxcol) {
                that.columnModel.push(that.checkboxColumn)
                if (options.showCheckboxcol === 'lock') that.checkboxColumn.initLock = true

                $th = $('<th class="' + that.classnames.td_checkbox + '"' + _rowspan + '><input type="checkbox" data-toggle="icheck"></th>')
                $th.prependTo($trs.first())
                if (_rowspan) $th.data('datagrid.column', that.checkboxColumn)
            }
            if (options.showLinenumber) {
                that.columnModel.unshift(that.linenumberColumn)
                if (options.showLinenumber === 'lock') that.linenumberColumn.initLock = true

                $th = $('<th class="' + that.classnames.td_linenumber + '"' + _rowspan + '>No.</th>')
                $th.prependTo($trs.first())
                if (_rowspan) $th.data('datagrid.column', that.linenumberColumn)
            }
            if (options.showChildcol || (options.showChildcol === undefined && options.hasChild && options.childOptions)) {
                that.columnModel.unshift(that.childColumn)

                $th = $('<th class="' + that.classnames.td_child + '"' + _rowspan + '>...</th>')
                $th.prependTo($trs.first())
                if (_rowspan) $th.data('datagrid.column', that.childColumn)
            }
            if (options.showEditbtnscol) {
                $th = $('<th' + _rowspan + '>' + that.editBtnsColumn.label + '</th>')
                $th.appendTo($trs.first())
                if (_rowspan) $th.data('datagrid.column', that.editBtnsColumn)
                that.columnModel[$th.index()] = that.editBtnsColumn
            }
        }

        if ($trs.length && trLen == 1) {
            $trs.find('> th').each(function (i) {
                var $th = $(this).addClass('single-row').data('index', i), op = $th.data('options'), oW = $th.attr('width') || 'auto', label = $th.html()
                oW = processWidth(oW);
                $th.removeAttr('width');
                if (that.columnModel.length && that.columnModel[i]) {
                    op = that.columnModel[i]
                    op.index = i
                } else {
                    if (op && typeof op === 'string') op = op.toObj()
                    if (typeof op !== 'object') op = {}

                    if (!op.name && $th.data('name')) {
                        op.name = $th.data('name');
                    }
                    op.index = i
                    op.label = label
                    op.width = (typeof op.width === 'undefined') ? oW : op.width

                    op = that.tools.setOp(op)

                    that.columnModel[i] = op
                }

                that.columnModel[i].th = $th

                $th.html('<div><div class="datagrid-space"></div><div class="datagrid-label">' + label + '</div><div class="' + that.classnames.th_cell + '"><div class="' + that.classnames.th_resizemark + '"></div></div></div>')
                if (op.menu && options.columnMenu) $th.addClass(that.classnames.th_menu)
                if (options.fieldSortable && op.quicksort) $th.addClass('datagrid-quicksort-th')
                if (op.align) $th.attr('align', op.align)
            })
        } else { // multi headers
            $trs.each(function () {
                var next_rows = [], next_ths = [], index = -1, next_index = 0

                if (rows.length) {
                    next_rows = rows.concat()
                    next_ths = ths.concat()
                }
                rows = []
                ths = []

                $(this).find('> th').each(function (i) {
                    var $th = $(this), op = $th.data('options') || $th.data('datagrid.column') || {}, colspan = parseInt(($th.attr('colspan') || 0), 10), rowspan = parseInt(($th.attr('rowspan') || 0), 10), oW = $th.attr('width') || 'auto', label = $th.html()
                    oW = processWidth(oW);
                    $th.removeAttr('width');
                    if (op && typeof op === 'string') op = op.toObj()
                    if (typeof op !== 'object') op = {}
                    if (!op.name && $th.data('name')) {
                        op.name = $th.data('name');
                    }
                    if ($.zui.isIE(8) && colspan === 1) colspan = 0

                    op.label = label
                    op.th = $th
                    if (op.gridCheckbox) op.label = 'Checkbox'

                    index++
                    if (colspan) {
                        op.colspan = colspan
                        for (var start_index = (next_rows.length ? next_rows[index] : index), k = start_index; k < (start_index + colspan); k++) {
                            rows[next_index++] = k
                            ths[next_index - 1] = op
                        }
                        index += (colspan - 1)

                        $th.data('index', index)

                        if (next_rows.length) {
                            op.parent = next_ths[index]
                        }
                    }
                    if (!rowspan || rowspan == 1) $th.addClass('single-row')
                    if (!colspan) {
                        op.width = (typeof op.width === 'undefined') ? oW : op.width

                        op = that.tools.setOp(op)
                        $th.html('<div><div class="datagrid-space"></div><div class="datagrid-label">' + label + '</div><div class="' + that.classnames.th_cell + '"><div class="' + that.classnames.th_resizemark + '"></div></div></div>')

                        if (op.menu && options.columnMenu) $th.addClass(that.classnames.th_menu)
                        if (options.fieldSortable && op.quicksort) $th.addClass('datagrid-quicksort-th')
                        if (op.align) $th.attr('align', op.align)
                        if (!next_rows.length) {
                            op.index = index
                            that.columnModel[index] = op
                        } else {
                            op.index = next_rows[index]
                            op.parent = next_ths[index]
                            that.columnModel[next_rows[index]] = op
                        }

                        $th.data('index', op.index)
                    } else {
                        $th.html('<div><div class="datagrid-space"></div><div class="datagrid-label">' + label + '</div><div class="' + that.classnames.th_cell + '"><div class="' + that.classnames.th_resizemark + '"></div></div></div>')
                    }
                })
            })
        }
    }

    // create thead by columns
    Datagrid.prototype.createThead = function () {
        var that = this, options = that.options, columns = options.columns, rowArr = [], rows = [], label, align, width, colspan, rowspan, resize, menu,
            columns2Arr = function (columns, rowArr, index, parent) {
                if (!rowArr) rowArr = []
                if (!index)   index = 0
                if (!rowArr[index]) rowArr[index] = []

                $.each(columns, function (i, n) {
                    var len = rowArr[index].length, colspan

                    if (parent) n.parent = parent
                    if (n.columns) {
                        colspan = n.columns.length
                        if (index && n.parent) {
                            that.tools.setColspan(n.parent, colspan)
                        }

                        n.index = that.columnModel.length + colspan - 1
                        n.colspan = colspan
                        n.quicksort = false
                        rowArr[index][len++] = n

                        return columns2Arr(n.columns, rowArr, index + 1, n)
                    } else {
                        n.rowspan = index
                        n.index = that.columnModel.length

                        n = that.tools.setOp(n)

                        rowArr[index][len++] = n
                        that.columnModel.push(n)
                    }
                })

                return rowArr
            }

        that.tools.appendColumns()

        if (options.showCheckboxcol) {
            columns.unshift(that.checkboxColumn)
            if (options.showCheckboxcol === 'lock') that.checkboxColumn.initLock = true
        }
        if (options.showLinenumber) {
            columns.unshift(that.linenumberColumn)
            if (options.showLinenumber === 'lock') that.linenumberColumn.initLock = true
        }
        if (options.showChildcol || (options.showChildcol === undefined && options.hasChild && options.childOptions)) {
            columns.unshift(that.childColumn)
        }
        if (options.showEditbtnscol) columns.push(that.editBtnsColumn)

        rowArr = columns2Arr(columns, rowArr)
        // the last model can't lock
        that.columnModel[that.columnModel.length - (options.showEditbtnscol ? 2 : 1)].lock = false
        // hidden fields
        if (options.hiddenFields) that.hiddenFields = options.hiddenFields
        // create thead
        that.$thead = $('<thead></thead>')
        $.each(rowArr, function (i, arr) {
            var $tr = $('<tr style="height:33px;"></tr>'), $num = '<th class="datagrid-number"></th>', $th

            $.each(arr, function (k, n) {
                label = n.hasOwnProperty('label')?n.label:n.name
                align = ' align="left"';//n.align ? (' align="' + n.align + '"') : ''
                width = n.width ? (' width="' + n.width + '"') : ''
                colspan = n.colspan ? ' colspan="' + n.colspan + '"' : ''
                rowspan = (rowArr.length - n.rowspan > 1) ? ' rowspan="' + (rowArr.length - n.rowspan) + '"' : ''
                resize = '<div class="' + that.classnames.th_resizemark + '"></div>'
                menu = ''

                if (n.gridCheckbox) label = '<input type="checkbox" data-toggle="icheck">'
                if (n.colspan) align = ' align="center"'
                if (n.thalign) align = ' align="'+ n.thalign +'"'
                if (n.menu && options.columnMenu) menu = ' class="'+ that.classnames.th_menu +'"'

                $th = $('<th' + menu + width + align + colspan + rowspan + '><div><div class="datagrid-space"></div><div class="datagrid-label">' + label + '</div><div class="' + that.classnames.th_cell + '">' + resize + '</div></div></th>')
                $th.data('index', n.index).appendTo($tr)

                if (!rowspan) $th.addClass('single-row')
                if (n.gridChild) $th.addClass(that.classnames.td_child)
                if (n.gridNumber) $th.addClass(that.classnames.td_linenumber)
                if (n.gridCheckbox) $th.addClass(that.classnames.td_checkbox)
                if (options.fieldSortable && n.quicksort) $th.addClass('datagrid-quicksort-th')

                n.th = $th
            })

            $tr.appendTo(that.$thead)
        })

        that.$thead.appendTo(that.$element).initui()
    }

    Datagrid.prototype.createTbody = function () {
        var that = this, options = that.options, data = options.data, model = that.columnModel, cols = [], pageInfo = $.zui.pageInfo, paging = that.paging, postData = {}

        if (data) {
            if (typeof data === 'string') {
                if (data.trim().startsWith('[') || data.trim().startsWith('{')) {
                    data = data.toObj()
                } else {
                    data = data.toFunc()
                }
            }
            if (typeof data === 'function') {
                data = data.call()
            }

            options.data = data
            that.tools.createTrsByData(data)
        } else if (options.dataUrl) {
//            if (!options.postData || !options.postData[pageInfo.pageSize]) {
//                postData[pageInfo.pageSize] = paging.pageSize
//                postData[pageInfo.pageVar] = paging.pageCurrent
//            }
            that.tools.loadData()
        }
    }

    Datagrid.prototype.refresh = function () {
        var that = this, options = that.options, tools = that.tools, isDom = that.isDom, pageInfo = $.zui.pageInfo, paging = that.paging, postData = {}

        if (!options.dataUrl) {
            if (options.data && options.data.length) {
                tools.initTbody(that.allData, true)
                return
            }

            $.zui.debug('Datagrid Plugin: Not Set the dataUrl option!')
            return
        }

        if (!options.postData || !options.postData[pageInfo.pageSize]) {
            postData[pageInfo.pageSize] = paging.pageSize
            postData[pageInfo.pageVar] = paging.pageCurrent
        }

        tools.loadData(postData, true)

        that.remoteDatas = null
        // clear sort
        that.$thead.find('> tr > th.datagrid-quicksort-th').find('> div > .datagrid-label > i').remove()
    }

    Datagrid.prototype.refreshChild = function (row) {
        var that = this, $trs = that.$tbody.find('> tr'), $table,
            refresh = function (obj) {
                $table = obj.data('zui.datagrid.child')

                if ($table && $table.length && $table.isTag('table'))
                    $table.datagrid('refresh')
            }

        if (row instanceof jQuery) {
            row.each(function () {
                refresh($(this))
            })
        } else if (isNaN(row)) {
            $.each(row.split(','), function (i, n) {
                if (n * 2 < $trs.length)
                    refresh($trs.eq(n * 2))
            })
        } else {
            if (row * 2 < $trs.length)
                refresh($trs.eq(row * 2))
        }
    }

    // api
    Datagrid.prototype.showChild = function (row, flag, func) {
        if (typeof flag === 'undefined')
            flag = true

        var that = this, options = that.options, $child, $td, $table, index, trData, childOptions, dataUrl, $trs = that.$tbody.find('> tr:not(.' + that.classnames.tr_child + ')'),
            fixedH = function () {
                if (!options.fullGrid)
                    that.fixedWidth()

                that.fixedHeight()
            },
            fixedL = function (index, showorhide, height) {
                if (that.$lockTableB) {
                    var $locktr = that.$lockTableB.find('> tbody > tr:eq(' + index + ')')

                    $locktr.toggle(showorhide)

                    if (height)
                        $locktr.height(height)
                    else if (!$locktr.height())
                        $locktr.height($child.height())
                }
            },
            show = function ($tr, $child, $table) {
                $td.find('> div').html($.zui.doRegional(FRAG.gridShrinkBtn, that.regional))

                $child.fadeIn('normal', function () {
                    if (!$table.data('zui.datagrid.init')) {
                        childOptions = $.extend(true, {}, that.childOptions), dataUrl = childOptions.dataUrl

                        if (that.isDom) trData = $tr.data('initData') || that.tools.setDomData($tr)
                        else {
                            trData = that.data[that.tools.getNoChildDataIndex($tr.index())]
                        }

                        if (dataUrl && !dataUrl.isFinishedTm()) {
                            dataUrl = that.tools.replacePlh(dataUrl, trData)

                            if (!dataUrl.isFinishedTm()) {
                                $.zui.debug('Datagrid Plugin: The datagrid options \'childOptions\' in the \'dataUrl\' options is incorrect: ' + dataUrl)
                            } else {
                                childOptions.dataUrl = dataUrl
                            }
                        }

                        $table
                            .datagrid(childOptions)
                            .data('zui.datagrid.parent', $tr)
                            .on('completed.zui.datagrid', $.proxy(function () {
                                fixedH()
                                fixedL(index, true, $child.outerHeight())

                                if (func)
                                    func.apply(that, [$table])
                            }, that))
                            .on('zui.datagrid.child.resize', function () {
                                $table
                                    .datagrid('fixedWidth')
                                    .datagrid('fixedHeight')
                            })

                        $tr.data('zui.datagrid.child', $table)
                    } else {
                        fixedH()
                        fixedL(index, true)

                        if (func)
                            func.apply(that, [$table])
                    }
                })
            },
            hide = function ($tr, $child, $table) {
                $child.fadeOut('normal', function () {
                    $td.find('> div').html($.zui.doRegional(FRAG.gridExpandBtn, that.regional))

                    fixedH()
                    fixedL(index, false)

                    if (func)
                        func.apply(that, [$table])
                })
            },
            showhide = function ($tr) {
                $child = $tr.next('.' + that.classnames.tr_child)

                if ($child && $child.length) {
                    $td = $tr.find('> td.' + that.classnames.td_child)
                    index = $child.index()
                    $table = $child.find('> td').find('table.table-child')

                    if ($table && $table.length) {
                        if ($child.is(':visible')) {
                            if (!flag) hide($tr, $child, $table)
                        } else {
                            if (flag) show($tr, $child, $table)
                        }
                    }
                }
            }

        if (func) {
            if (typeof func === 'string')
                func = func.toFunc()
            if (typeof func !== 'function')
                func = false
        }

        if (row instanceof jQuery) {
            row.each(function () {
                showhide($(this))
            })
        } else if (isNaN(row)) {
            $.each(row.split(','), function (i, n) {
                var tr = $trs.eq(parseInt(n.trim(), 10))

                if (tr && tr.length)
                    showhide(tr)
            })
        } else if (!isNaN(row)) {
            var tr = $trs.eq(row)

            if (tr && tr.length)
                showhide(tr)
        }
    }

    Datagrid.prototype.reload = function (option) {
        var options = this.options

        if (option && typeof option === 'object') {
            if (option.clearOldPostData)
                delete options.postData

            $.extend(true, options, option)
        }

        if (!options.dataUrl && options.data) {
            this.allData = options.data
        }

        this.refresh()
    }

    Datagrid.prototype.destroy = function () {
        var that = this, $element = that.$grid.data('zui.datagrid.table')

        if ($element) {
            that.$element.html($element.html()).insertBefore(that.$grid)
            that.$grid.remove()
        }
    }

    Datagrid.prototype.initTop = function () {
        var that = this, options = that.options, regional = that.regional, hastoolbaritem = false, $group, groupHtml = '<div class="btn-group" role="group"></div>', btnHtml = '<button type="button" class="btn" data-icon=""></button>'

        if (options.showToolbar) {
            if (options.toolbarItem || options.toolbarCustom) {
                if (options.toolbarItem) {
                    var itemFunc = options.toolbarItem.toFunc()

                    if (typeof itemFunc === 'function') {
                        options.toolbarItem = itemFunc.apply()
                    }

                    hastoolbaritem = true
                    if (options.toolbarItem.indexOf('all') >= 0) options.toolbarItem = 'add, edit, |, enable, disable, |, del, |, import, export, |, refresh'
                    $.each(options.toolbarItem.split(','), function (i, n) {
                        n = n.trim().toLocaleLowerCase()
                        if (!$group || n === '|') {
                            $group = $(groupHtml).appendTo(that.$toolbar)
                            if (n === '|') return true
                        }

                        if (n === 'add') {
                            if(options.editUrl){
                                that.$toolbar_add = $(btnHtml).attr('data-icon', 'plus').addClass('btn-primary').text($.zui.getRegional('datagrid.add'))
                                    .appendTo($group)
                                    .on('click', function (e) {
                                        that.doAddRow()
                                    })
                            }
                        } else if (n === 'edit') {
                            if(options.editUrl){
                                that.$toolbar_edit = $(btnHtml).attr('data-icon', 'edit').addClass('btn-success').text($.zui.getRegional('datagrid.edit'))
                                    .appendTo($group)
                                    .on('click', function (e) {
                                        var $selectTrs = that.$tbody.find('> tr.' + that.classnames.tr_selected)
                                        if (!$selectTrs.length) {
                                            $(this).alertmsg('info', $.zui.getRegional('datagrid.selectMsg'))
                                        } else {
                                            if (that.$lastSelect)
                                                that.doEditRow(that.$lastSelect)
                                            else
                                                that.doEditRow($selectTrs.first())
                                        }
                                    })
                            }
                        } else if (n === 'del') {
                            if(options.delUrl){
                                that.$toolbar_del = $(btnHtml).attr('data-icon', 'times').addClass('btn-danger').text($.zui.getRegional('datagrid.del'))
                                    .appendTo($group)
                                    .on('click', function (e) {
                                        var $selectTrs = that.$tbody.find('> tr.' + that.classnames.tr_selected)

                                        if ($selectTrs.length) {
                                            that.delRows($selectTrs)
                                        } else {
                                            $(this).alertmsg('info', $.zui.getRegional('datagrid.selectMsg'))
                                        }
                                    })
                            }
                        } else if (n === 'enable') {
                            if(options.enableUrl){
                                that.$toolbar_del = $(btnHtml).attr('data-icon', 'checked').addClass('btn-success').text($.zui.getRegional('datagrid.enable'))
                                    .appendTo($group)
                                    .on('click', function (e) {
                                        var $selectTrs = that.$tbody.find('> tr.' + that.classnames.tr_selected)

                                        if ($selectTrs.length) {
                                            that.changeRowsStatus(options.enableUrl, $selectTrs)
                                        } else {
                                            $(this).alertmsg('info', $.zui.getRegional('datagrid.selectMsg'))
                                        }
                                    })
                            }
                        } else if (n === 'disable') {
                            if(options.disableUrl){
                                that.$toolbar_del = $(btnHtml).attr('data-icon', 'check-empty').addClass('btn-warning').text($.zui.getRegional('datagrid.disable'))
                                    .appendTo($group)
                                    .on('click', function (e) {
                                        var $selectTrs = that.$tbody.find('> tr.' + that.classnames.tr_selected)

                                        if ($selectTrs.length) {
                                            that.changeRowsStatus(options.disableUrl, $selectTrs)
                                        } else {
                                            $(this).alertmsg('info', $.zui.getRegional('datagrid.selectMsg'))
                                        }
                                    })
                            }
                        } else if (n === 'refresh') {
                            that.$toolbar_refresh = $(btnHtml).attr('data-icon', 'refresh').addClass('btn-success').text($.zui.getRegional('datagrid.refresh'))
                                .appendTo($group)
                                .on('click', function (e) {
                                    that.refresh()
                                })
                        } else if (n === 'import') {
                            if(options.importOption)
                                that.$toolbar_add = $(btnHtml).attr('data-icon', 'signin').addClass('btn-info').text($.zui.getRegional('datagrid.import'))
                                    .appendTo($group)
                                    .on('click', function (e) {
                                        var opts = options.importOption

                                        if (typeof opts === 'string')
                                            opts = opts.toObj()

                                        if (opts.options && opts.options.url) {
                                            if (opts.type === 'dialog') {
                                                that.$grid.dialog(opts.options)
                                            } else if (opts.type === 'navtab') {
                                                that.$grid.navtab(opts.options)
                                            } else {
                                                that.$grid.zuiajax('doajax', opts.options)
                                            }
                                        }
                                    })
                        } else if (n === 'export') {
                            if (options.exportOption) {
                                that.$toolbar_add = $(btnHtml).attr('data-icon', 'signout').addClass('btn-success').text($.zui.getRegional('datagrid.export'))
                                    .appendTo($group)
                                    .on('click', function (e) {
                                        that.export('init');
                                    })
                            }
                        }
                    })
                }

                if (options.toolbarCustom) {
                    var $custom, $custombox = $('<div style="display:table-cell;"></div>')

                    if (typeof options.toolbarCustom === 'string') {
                        var custom = $(options.toolbarCustom)

                        if (custom && custom.length) {
                            $custom = custom
                        } else {
                            custom = custom.toFunc()
                            if (custom) {
                                $custom = custom.call(that)
                                if (typeof $custom === 'string') $custom = $($custom)
                            }
                        }
                    } else if (typeof options.toolbarCustom === 'function') {
                        $custom = options.toolbarCustom.call(that)
                        if (typeof $custom === 'string') $custom = $($custom)
                    } else {
                        $custom = options.toolbarCustom
                    }

                    if ($custom && $custom.length && typeof $custom !== 'string') {
                        if (hastoolbaritem) {
                            $custombox.css('margin-left', '5px')
                        }
                        $custombox.appendTo(that.$toolbar)
                        $custom.appendTo($custombox)
                    }
                }

                that.$toolbar.initui()
            }
        }
    }

    Datagrid.prototype.initThead = function () {
        var that = this, options = that.options, tools = that.tools, columnModel = that.columnModel, width, cols = [], $tableB, $grid = $('<div class="zui-datagrid"></div>'), $firstTr, rowspan

        that.$tableH.append(that.$thead)

        that.init_thead = true
        that.$trsH = that.$thead.find('> tr')
        that.table_width = 0
        that.$colgroupH = that.$tableH.find('> colgroup')
        that.fixtedColumnWidthCount = 0

        $.each(that.columnModel, function (i, n) {
            var lockWidth = ''

            if (n === that.linenumberColumn || n === that.checkboxColumn || n === that.childColumn || n === that.editBtnsColumn)
                lockWidth = ' class="datagrid_col_lockwidth"'

            width = n.width

            if (!width || width === 'auto') {
                if (!$tableB) {
                    $tableB = that.$element.clone().addClass('table table-bordered').appendTo($grid)
                    $tableB.prepend(that.$thead.clone())
                    $grid.appendTo($('body'))
                    $tableB.attr('style', 'width:auto !important;').initui()
                }

                that.fixtedColumnWidthCount++
                n.fixedWidth = true

                $firstTr = $tableB.find('> tbody > tr:first-child')

                if ($firstTr.hasClass("datagrid-nodata")) {
                    if (that.$trsH.length > 1)
                        rowspan = that.$trsH.length - parseInt(n.th.attr('rowspan') || 1, 10)
                    else
                        rowspan = 0

                    width = $tableB.find('> thead > tr:eq(' + rowspan + ') > th:eq(' + n.th.index() + ')').outerWidth() + 5
                } else {
                    width = ($tableB.find('> tbody > tr:first-child > td:eq(' + i + ')').outerWidth()) + 5
                }
            }

            cols.push('<col style="width:' + width + 'px;"' + lockWidth + '>')
            n.width = width
            that.table_width += width
        })

        if ($grid)
            $grid.remove()

        that.$colgroupH.html(cols.join(''))

        // fixed div height for th
        setTimeout(function () {
            var maxH = 0,
                //boxH = that.$boxB.height(),boxT = parseInt(that.$boxM.css('top')),
                needFix = false;
            that.$thead.find('> tr').each(function () {
                $(this).find('> th').each(function () {
                    var $th = $(this), height = $th.outerHeight(), $div = $th.find('> div').first(), rows = $th.attr('rowspan')
                    //todo 具体原因未明，每个th的div会顺延加1所以只好这里固定了
                    if(rows>1) {
                        rows = parseInt(rows);
                    } else {
                        if(height<33) {
                            //boxT = height;
                            needFix = true
                            height = 33;
                        }
                        rows = parseInt(height / 33);
                    }
                    height = rows * 33 + rows - 1;
                    maxH = Math.max(maxH, height)
                    $div.height(height);
                    $th.height(height);
                })
            });
            if(needFix) {
                that.tools.setBoxbH()
//                boxH += boxT - maxH
//                that.$boxB.height(boxH)
//                that.$boxM.height(boxH).css({top: maxH})
            }
        }, 10)

        // thead - events
        var $ths = that.$trsH.find('> th')
        // events - quicksort
        $ths.filter('.datagrid-quicksort-th')
            .on('click.zui.datagrid.quicksort', function (e) {
                var $target = $(e.target)

                if (!$target.closest('.' + that.classnames.th_cell).length && !that.isResize)
                    tools.quickSort(columnModel[$(this).data('index')])
            })

        // events - contextmenu
        if (options.contextMenuH) {
            tools.contextmenuH()
        }

        that.initTbody()

        if (options.columnResize) that.colResize()
        if (options.columnMenu)   that.colMenu()
        if (options.paging||options.checkAction) that.initPaging()
        if (options.editEnabled)     that.showActionButton()
        if (!that.$element.hasClass('table-child'))
            that.resizeGrid()

        var delayFunc = function () {
            if (options.showTfoot) that.initTfoot()

            tools.setBoxbH()

            /* render */
            if (that.renderTds && that.renderTds.length) {
                var $trs = that.$tbody.find('> tr:not(.' + that.classnames.tr_child + ', .datagrid-nodata)'), j = that.renderTds && that.renderTds.length

                for (var i = 0; i < j; i++) {
                    var obj = that.renderTds[i], label = obj.render.call(that, obj.label, obj.trData, that.columnModel[obj.tdindex].items)

                    $trs.eq(obj.trindex).find('> td:eq(' + obj.tdindex + ')').html('<div>' + label + '</div>')
                }
            }

            that.initEvents()
            that.$tableH.initui()
            that.$tableB.initui()
            that.$lockTableB && that.$lockTableB.initui()

            setTimeout(function () {
                that.fixedWidth('init')
                that.initLock()
                that.$element.trigger('completed.zui.datagrid', {datas: that.data})
                that.$boxM && that.$boxM.trigger('zui.ajaxStop').hide()
            }, 50)
        }

        delayFunc()
    }

    Datagrid.prototype.fixedWidth = function (isInit) {
        var that = this, options = that.options, bW, excludeW = 0, fixedW, columnModel = that.columnModel, length = columnModel.length

        if (isInit && that.initFixedW) return
        that.initFixedW = true

        var setNewWidth = function () {
            if (String(that.options.width).endsWith('%'))
                that.$grid.css('width', '')

            that.$boxH.find('> div').css('width', '')
            that.$boxB.find('> div').css('width', '')
            that.$boxF && that.$boxF.find('> div').css('width', '')
            that.$boxP && that.$boxP.find('> div.paging-content').css('width', '')

            //bW = that.$boxB.find('> div').width()
            bW = that.$boxB.width() - 1;        //修正一下，不知道为什么子div中的宽度会少10个像素
            if (that.options.hasChild) {
                that.$boxB.scrollTop(5)

                //扣除竖向滚动条的宽度
                //if (that.$boxB.scrollTop())
                //    bW = bW - 18
            }

            if (bW) {
                that.$boxB.find('> div').width(bW)
                that.$boxH.find('> div').width(bW)
                that.$boxF && that.$boxF.find('> div').width(bW)

                //if (that.table_width > bW || options.fullGrid)
                //    that.$boxP && that.$boxP.find('> div.paging-content').width(bW)
                //else
                //    that.$boxP && that.$boxP.find('> div.paging-content').width(that.table_width)
            }

            if (options.fullGrid) {
                var $firstTr = that.$tbody.find('> tr:first'), width, rowspan, $cols = that.$colgroupB.find('> col'), $col

                $cols.filter(':not(.datagrid_col_lockwidth)').css('width', '')

                $.each(that.columnModel, function (i, n) {
                    if (n === that.linenumberColumn || n === that.checkboxColumn || n === that.childColumn || n === that.editBtnsColumn) {
                        return true
                    }

                    if ($firstTr.hasClass("datagrid-nodata")) {
                        if (that.$trsH.length > 1)
                            rowspan = that.$trsH.length - parseInt(n.th.attr('rowspan') || 1, 10)
                        else
                            rowspan = 0

                        width = that.$trsH.eq(rowspan).find('> th:eq(' + n.th.index() + ')').width()
                    } else {
                        width = ($firstTr.find('> td:eq(' + i + ')').width())
                    }
                    //修正锁定时宽度计算的问题
                    that.columnModel[i].width = width
                    $cols.eq(i).css('width', width)
                })

                that.$colgroupH.html(that.$colgroupB.html())
            }
        }

        setNewWidth()
    }

    Datagrid.prototype.fixedHeight = function (height) {
        var that = this, options = that.options
        if (options.height === 'auto') {
            that.boxH = 'auto'
            that.$grid.css('height', '')
            that.$boxB.css('height', '')
            that.tools.setBoxbH()
        } else
            that.tools.setBoxbH(height)

        // if scrollLeft
        that.$boxB.scrollLeft(5)

        if (that.$boxB.scrollLeft())
            that.fixedWidth()
    }

    Datagrid.prototype.initTbody = function () {
        var that = this, options = that.options, tools = that.tools, $trs = that.$tbody.find('> tr'), $tds = $trs.find('> td'), width = '0'
        that.init_tbody = true
        that.$colgroupB = that.$colgroupH.clone()
        that.$tableB.prepend(that.$colgroupB)

        if (options.fullGrid) width = '100%'

        that.$tableH.css('width', width)
        that.$tableB.css('width', width)

        // add class
        that.$tableB.removeAttr('data-toggle width').addClass('table table-bordered').removeClass('table-hover').show()

        that.$boxB
            .scroll(function () {
                that.$boxH.find('> div').prop('scrollLeft', this.scrollLeft)
                that.$boxF && that.$boxF.find('> div').prop('scrollLeft', this.scrollLeft)
                that.$lockB && that.$lockB.prop('scrollTop', this.scrollTop)
            })

        // if DOM to datagrid
        if (that.isDom) {
            that.tools.appendColumns()
            if (options.showLinenumber) {
                that.showLinenumber(options.showLinenumber)
            }
            if (options.showCheckboxcol) {
                that.showCheckboxcol(options.showCheckboxcol)
            }
            if (options.showEditbtnscol) {
                that.showEditCol(options.showEditbtnscol)
            }

            that.$grid.data(that.datanames.tbody, that.$tbody.clone())
        }
    }

    // init events(only tbody)
    Datagrid.prototype.initEvents = function ($trs) {
        var that = this, options = that.options, trs = that.$tbody.find('> tr')

        if (!$trs) $trs = trs

        $trs.on('click.zui.datagrid.tr', function (e, checkbox) {
            var $tr = $(this), index = $tr.index(), data, $selectedTrs = that.$tbody.find('> tr.' + that.classnames.tr_selected), $last = that.$lastSelect, checked, $lockTrs = that.$lockTbody && that.$lockTbody.find('> tr')

            if (checkbox) {
                checked = checkbox.is(':checked')
                if (!checked) that.$lastSelect = $tr
                that.selectedRows($tr, !checked)
            } else {
                if (options.selectMult) {
                    that.selectedRows($tr)
                } else {
                    if (!$.zui.KeyPressed.ctrl && !$.zui.KeyPressed.shift) {
                        if ($selectedTrs.length > 1 && $tr.hasClass(that.classnames.tr_selected)) {
                            that.selectedRows($selectedTrs.not($tr))
                            that.$lastSelect = $tr
                        } else {
                            if ($selectedTrs.length && $selectedTrs[0] != this) that.selectedRows(null)
                            if (!$tr.hasClass(that.classnames.tr_selected)) that.$lastSelect = $tr
                            that.selectedRows($tr)
                        }
                    } else {
                        window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty() //clear selection

                        if ($.zui.KeyPressed.ctrl) {
                            if (!$tr.hasClass(that.classnames.tr_selected)) that.$lastSelect = $tr
                            that.selectedRows($tr)
                        } else if ($.zui.KeyPressed.shift) {
                            if (!$last) $last = that.$lastSelect = $tr
                            if ($last.length) {
                                that.selectedRows(null)
                                if ($last.index() != index) {
                                    if ($last.index() > index) {
                                        that.selectedRows($tr.nextUntil($last).add($tr).add($last), true)
                                    } else {
                                        that.selectedRows($tr.prevUntil($last).add($tr).add($last), true)
                                    }
                                } else {
                                    that.selectedRows(index)
                                }
                            }
                        }
                    }
                }
            }

            if (that.isDom)
                data = that.tools.setDomData($tr)
            else
                data = that.data[that.tools.getNoChildDataIndex(index)]

            that.$element.trigger('clicked.zui.datagrid.tr', {data: data})
        })
            .on('mouseenter.zui.datagrid', function (e) {
                var $tr = $(this), index = $tr.index()

                $tr.addClass('datagrid-hover')
                that.$lockTbody && that.$lockTbody.find('> tr:eq(' + index + ')').addClass('datagrid-hover')
            })
            .on('mouseleave.zui.datagrid', function (e) {
                var $tr = $(this), index = $tr.index()

                $tr.removeClass('datagrid-hover')
                that.$lockTbody && that.$lockTbody.find('> tr:eq(' + index + ')').removeClass('datagrid-hover')
            })
            // custom event - delete
            .on('zui.datagrid.tr.delete', function (e) {
                e.stopPropagation()

                var $tr = $(this), tr_index = $tr.index(), data_index = tr_index, data = that.data, gridIndex, allData = that.allData, $lockTrs = that.$lockTbody && that.$lockTbody.find('> tr')

            if ($tr.hasClass('datagrid-nodata')) return false

                if (that.options.hasChild && that.options.childOptions) {
                    data_index = data_index / 2

                    // remove child dom
                    $tr.next().remove()
                    $lockTrs && $lockTrs.eq(tr_index).next().remove()
                }
                if (!that.isDom) {
                    gridIndex = data[data_index].gridIndex
                    that.data = data.remove(data_index)     // remove data in the current page data
                    that.allData = allData.remove(gridIndex)   // remove data in allData
                }

                /* update gridNumber */
                if ($.inArray(that.linenumberColumn, that.columnModel) != -1) {
                    $tr.nextAll(':not(.' + that.classnames.tr_child + ')').each(function () {
                        var $td = $(this).find('> td.' + that.classnames.td_linenumber), num = parseInt($td.text(), 10)

                        $td.text(num - 1)
                    })

                    $lockTrs && $lockTrs.eq(tr_index).trigger('zui.datagrid.tr.delete', [tr_index])
                }

                // remove dom
                $tr.remove()
                $lockTrs && $lockTrs.eq(tr_index).remove()

                // no data
                that.tools.createNoDataTr()
            })

        // child
        that.$grid.off('click.zui.datagrid.tr.child').on('click.zui.datagrid.tr.child', 'td.' + that.classnames.td_child, function (e) {
            e.stopPropagation()

            var $this = $(this), $tr = $this.closest('tr'), $child = $tr.next('.' + that.classnames.tr_child)

            if ($child && $child.length)
                that.showChild($tr, !$child.is(':visible'))
        })
            // td checkbox
            .off('ifClicked').on('ifClicked', 'td.' + that.classnames.td_checkbox + ' input', function (e) {
            e.stopPropagation()

            var $this = $(this), $tr = $this.closest('tr'), tr_index = $tr.index()

            that.$tbody.find('> tr:eq(' + tr_index + ')').trigger('click.zui.datagrid.tr', [$this])
        })
            // th checkbox - check all
            .off('ifChanged').on('ifChanged', 'th.' + that.classnames.td_checkbox + ' input', function (e) {
            e.stopPropagation()

            var checked = $(this).is(':checked'), $trs = that.$tbody.find('> tr:not(".' + that.classnames.tr_child + '")')

            that.selectedRows($trs, checked)
        })
            .off('click.zui.datagrid').on('click.zui.datagrid', function(e){
                //只好用一个笨办法来关闭现在打开的菜单了
                var elem = $(e.toElement).closest('div');
                if(elem.hasClass('datagrid-column-menu-btn') || elem.hasClass('datagrid-menu-box')) {
                    return false;
                } else {
                    that.$menu.hide();
                }
        });


        //contextmenu
        if (options.contextMenuB) {
            $trs.each(function () {
                that.tools.contextmenuB($(this))
            })
        }
    }

    // 解析操作项
    Datagrid.prototype.parseAction = function(action){
        var actions = [];
        var _tmp = action.split(',');
        $.each(_tmp, function () {
            var _tmp1 = this.split('|');
            var _name = _tmp1[0];
            var _title = _tmp1[0];
            var _icon = '';
            if (_tmp1.length > 2) {
                _icon = '<i class="icon icon-' + _tmp1[2] + '"></i>';
            }
            if (_tmp1.length > 1) {
                _title = _tmp1[1];
            }
            if ($.isExitsFunction(_name)) {
                actions.push('<button class="btn" onclick="' + _name + '(this)" title="' + _title + '">' + _icon + '<span class="btn-title">' + _title + '</span></button>');
            }
        });
        action = '<div class="btn-group">'+actions.join('')+'</div>';
        return action;
    };

    Datagrid.prototype.initTfoot = function () {
        var that = this, options = that.options, tools = that.tools, columnModel = that.columnModel, $tr = $('<tr></tr>')

        that.$boxF = $('<div class="datagrid-box-f"></div>')
        that.$colgroupF = that.$colgroupH.clone()
        that.$tableF = that.$tableH.clone().empty()
        that.$tableF.append(that.$colgroupF)
        that.$boxF.append($('<div class="datagrid-wrap-h"></div>').append(that.$tableF))
        that.$boxF.insertAfter(that.$boxB)

        that.$tfoot = $('<thead></thead>')
        $.each(columnModel, function (i, n) {
            var $th = $('<th><div></div></th>')

            if (n.calc) {
                var calc_html = '<div><div class="datagrid-calcbox">#tit#</div>#number#</div>'

                if (n.calc === 'avg')
                    $th.html(calc_html.replace('#tit#', (n.calcTit || 'AVG')).replace('#number#', (n.calc_sum / n.calc_count).toFixed(n.calcDecimal || 2)))
                else if (n.calc === 'count')
                    $th.html(calc_html.replace('#tit#', (n.calcTit || 'COUNT')).replace('#number#', (n.calc_count)))
                else if (n.calc === 'sum')
                    $th.html(calc_html.replace('#tit#', (n.calcTit || 'SUM')).replace('#number#', (n.calc_sum)))
                else if (n.calc === 'max')
                    $th.html(calc_html.replace('#tit#', (n.calcTit || 'MAX')).replace('#number#', (n.calc_max)))
                else if (n.calc === 'min')
                    $th.html(calc_html.replace('#tit#', (n.calcTit || 'MIN')).replace('#number#', (n.calc_min)))
            }

            if (n.hidden) $th.css('display', 'none')

            $th.appendTo($tr)
        })

        that.$tfoot.append($tr).appendTo(that.$tableF)

        // events
        that.$tfoot.on('zui.datagrid.tfoot.resizeH', function () {
            tools.setBoxbH(options.height)

        })
    }

    // selected row
    Datagrid.prototype.selectedRows = function (rows, selected) {
        var that = this, $lockTrs = that.$lockTbody && that.$lockTbody.find('> tr'), $trs = that.$tbody && that.$tbody.find('> tr')
        var selectedTr = function (n) {
            if (typeof selected === 'undefined') selected = true
            if ($trs.eq(n).hasClass(that.classnames.tr_child)) return

            $trs.eq(n)
                .toggleClass(that.classnames.tr_selected, selected)
                .find('> td.' + that.classnames.td_checkbox + ' input').iCheck(selected ? 'check' : 'uncheck')

            $lockTrs && $lockTrs.eq(n)
                .toggleClass(that.classnames.tr_selected, selected)
                .find('> td.' + that.classnames.td_checkbox + ' input').iCheck(selected ? 'check' : 'uncheck')
        }

        if (rows === null) {
            $trs.removeClass(that.classnames.tr_selected)
                .find('> td.' + that.classnames.td_checkbox + ' input').iCheck('uncheck')

            $lockTrs && $lockTrs.removeClass(that.classnames.tr_selected)
                .find('> td.' + that.classnames.td_checkbox + ' input').iCheck('uncheck')
        } else if (typeof rows === 'object') {
            rows.each(function () {
                var $row = $(this), index = $row.index()

                if ($row.hasClass(that.classnames.tr_child)) return true

                if (typeof selected !== 'undefined') {
                    selectedTr(index)
                } else {
                    $row.toggleClass(that.classnames.tr_selected)
                        .trigger('zui.datagrid.tr.selected')
                        .find('> td.' + that.classnames.td_checkbox + ' input').iCheck(($row.hasClass(that.classnames.tr_selected) ? 'check' : 'uncheck'))

                    $lockTrs && $lockTrs.eq(index)
                        .toggleClass(that.classnames.tr_selected)
                        .trigger('zui.datagrid.tr.selected')
                        .find('> td.' + that.classnames.td_checkbox + ' input').iCheck(($row.hasClass(that.classnames.tr_selected) ? 'check' : 'uncheck'))
                }
            })
        } else if (isNaN(rows)) {
            if(typeof rows!=='undefined')
            $.each(rows.split(','), function (i, n) {
                selectedTr(parseInt(n.trim(), 10))
            })
        } else if (!isNaN(rows)) {
            selectedTr(parseInt(rows, 10))
        }

        if (that.$lastSelect && !that.$lastSelect.hasClass(that.classnames.tr_selected)) {
            that.$lastSelect = null
        }

        // selectedTrs
        if(that.$tbody)
            var $selectedTrs = that.$tbody.find('> tr.' + that.classnames.tr_selected);
        else var $selectedTrs = {};
        var datas = []

        if($selectedTrs.length)
        $selectedTrs.each(function () {
            var $tr = $(this), data_index = $tr.index(), data

            data_index = that.tools.getNoChildDataIndex(data_index)

            if (that.isDom) data = $tr.data('initData') || that.tools.setDomData($tr)
            else data = that.data[data_index]

            datas.push(data)
        })
        if(that.$boxP && that.$boxP.find('div.paging-content')) {
            if(datas.length){
                that.$boxP.find('div.paging-content').find('div.datagrid-checkaction').children().removeClass('disabled');
            } else {
                that.$boxP.find('div.paging-content').find('div.datagrid-checkaction').children().addClass('disabled');
            }
        }
        that.$element.data('selectedTrs', $selectedTrs).data('selectedDatas', datas)
    }

    //lock
    Datagrid.prototype.initLock = function () {
        var that = this

        that.col_lock_count = 0
        $.each(that.columnModel, function (i, n) {
            if (n.initLock) that.col_lock_count ++
        })

        if (that.col_lock_count) that.doLock()
    }

    //api - colLock
    Datagrid.prototype.colLock = function (column, lockFlag) {
        var that = this, $th, index, columnModel

        if ($.type(column) === 'number') {
            index = parseInt(column, 10)
            if (index < 0 || index > that.columnModel.length - 1) return
            columnModel = that.columnModel[index]
            $th = columnModel.th
        } else {
            $th = column
            index = $th.data('index')
            columnModel = that.columnModel[index]
        }

        if (columnModel === that.editBtnsColumn) return // edit btn column
        else if (columnModel.index === that.columnModel.length - 1) return // last column
        if (typeof columnModel.locked === 'undefined') columnModel.locked = false
        if (columnModel.locked === lockFlag) return

        columnModel.initLock = lockFlag

        if (lockFlag) {
            that.col_lock_count++
        } else {
            that.col_lock_count--
        }
        if (that.col_lock_count < 0) that.col_lock_count = 0

        that.doLock()
    }

    Datagrid.prototype.fixedLockItem = function (type) {
        var that = this, columnModel = that.columnModel, $lockTrs = that.$lockTbody && that.$lockTbody.find('> tr')

        // out
        if (!type) {
            var fixedTh = function ($th, $lockTh) {
                $lockTh.clone().insertAfter($lockTh)
                $lockTh.hide().insertAfter($th)
                $th.remove()
            }

            //thead checkbox
            if (that.$lockThead) {
                // checkbox
                if ($.inArray(that.checkboxColumn, columnModel) != -1 && that.checkboxColumn.locked) {
                    var $lockTh = that.$lockThead.find('> tr:first > th.' + that.classnames.td_checkbox), index = that.checkboxColumn.index, $th = that.$thead.find('> tr:first > th:eq(' + index + ')')

                    fixedTh($th, $lockTh)
                }
            }

            // checkbox td
            if ($lockTrs && $lockTrs.length) {
                $lockTrs.each(function () {
                    var $lockTr = $(this), tr_index = $lockTr.index(), $tr, $td,
                        fixedTd = function ($lockTd, tr_index, td_index) {
                            $tr = that.$tbody.find('> tr:eq(' + tr_index + ')')
                            $td = $tr.find('> td:eq(' + td_index + ')')
                            $lockTd.clone().insertAfter($lockTd)
                            $lockTd.hide().insertAfter($td)
                            $td.remove()
                        }

                    if ($.inArray(that.checkboxColumn, columnModel) != -1 && that.checkboxColumn.locked) {
                        $lockTr.find('> td.' + that.classnames.td_checkbox).each(function () {
                            var $lockTd = $(this), td_index = that.checkboxColumn.index

                            fixedTd($lockTd, tr_index, td_index)
                        })
                    }
                })
            }
        } else { //in
            var fixedTh = function ($th, $lockTh) {
                $th.clone().html('').insertAfter($th)
                $th.show().insertAfter($lockTh)
                $lockTh.remove()
            }

            //thead checkbox
            if (that.$lockThead) {
                // checkbox
                if ($.inArray(that.checkboxColumn, columnModel) != -1 && that.checkboxColumn.locked) {
                    var $lockTh = that.$lockThead.find('> tr:first > th.' + that.classnames.td_checkbox), index = that.checkboxColumn.index, $th = that.$thead.find('> tr:first > th:eq(' + index + ')')

                    fixedTh($th, $lockTh)
                }
            }

            // checkbox td
            if ($lockTrs && $lockTrs.length) {
                $lockTrs.each(function () {
                    var $lockTr = $(this), tr_index = $lockTr.index(), $tr, $td,
                        fixedTd = function ($lockTd, tr_index, td_index) {
                            $tr = that.$tbody.find('> tr:eq(' + tr_index + ')')
                            $td = $tr.find('> td:eq(' + td_index + ')')
                            $td.clone().insertAfter($td)
                            $td.show().insertAfter($lockTd)
                            $lockTd.remove()
                        }

                    if ($.inArray(that.checkboxColumn, columnModel) != -1 && that.checkboxColumn.locked) {
                        $lockTr.find('> td.' + that.classnames.td_checkbox).each(function () {
                            var $lockTd = $(this), td_index = that.checkboxColumn.index

                            fixedTd($lockTd, tr_index, td_index)
                        })
                    }
                })
            }
        }
    }

    //locking
    Datagrid.prototype.doLock = function () {
        var that = this, options = that.options, tools = that.tools, columnModel = that.columnModel, tableW = that.$tableH.width(), width = 0, $trs, $lockTrs, lockedLen = 0
        var hasFoot = that.$boxF && true, top = 0

        if (!that.$lockBox || !that.$lockBox.length) {
            that.$lockBox = $('<div class="datagrid-box-l"></div>')
            that.$lockH = $('<div class="datagrid-box-h"></div>')
            that.$lockB = $('<div class="datagrid-box-b"></div>')
            that.$lockF = $('<div class="datagrid-box-f"></div>')

            that.$lockTableH = $('<table class="table table-bordered"></table>')
            that.$lockTableB = $('<table></table>').addClass(that.$tableB.attr('class'))
            that.$lockTableF = $('<table class="table table-bordered"></table>')

            that.$lockH.append(that.$lockTableH)
            that.$lockB.append(that.$lockTableB)
            that.$lockF.append(that.$lockTableF)

            that.$lockBox.append(that.$lockH).append(that.$lockB).prependTo(that.$grid)
            if (hasFoot) {
                that.$lockBox.append(that.$lockF)
                that.$lockF.css('margin-top', (that.$boxB.outerHeight() - that.$boxB[0].clientHeight)).height(that.$boxF.outerHeight())
            }
        } else {
            that.fixedLockItem()
            that.$lockTableH.empty()
            that.$lockTableB.empty()
            that.$lockTableF && that.$lockTableF.empty()
        }

        if (that.$boxT)    top += that.$boxT.outerHeight()
        if (that.$toolbar) top += that.$toolbar.outerHeight()
        if (top) that.$lockBox.css({top:top})
        else {
            if(!that.$toolbar) {
                that.$lockBox.css({'border-top':'0px'});
            }
        }

        // display initLock columns, hide other
        $.each(columnModel, function (i, n) {
            n.lockShow = false
            n.lockHide = false
            if (n.initLock) {
                if (n.hidden) tools.showhide(n, true)
                n.lockHide = true
                n.locked = true
                n.lockIndex = lockedLen++
                width += n.width
            } else {
                n.lockShow = true
                if (!n.hidden) tools.showhide(n, false)
                else n.lockShow = false
                if (n.locked) n.lockShow = true
                n.locked = false
            }
        })

        that.$lockThead = that.$thead.clone(true)
        that.$lockTbody = that.$tbody.clone()
        that.$lockColgroupH = that.$colgroupH.clone()
        that.$lockColgroupB = that.$colgroupB.clone()

        that.$lockTableH.append(that.$lockColgroupH).append(that.$lockThead).css('width', width)
        that.$lockTableB.append(that.$lockColgroupB).append(that.$lockTbody).css('width', width)

        if (hasFoot) {
            that.$lockTfoot = that.$tableF.find('> thead').clone()
            that.$lockColgroupF = that.$colgroupF.clone()
            that.$lockTableF.append(that.$lockColgroupF).append(that.$lockTfoot).css('width', width)
        }

        // display unlock columns, hide locked columns
        $.each(that.columnModel, function (i, n) {
            if (n.lockShow) tools.showhide(n, true)
            if (n.lockHide) tools.showhide(n, false)
        })

        that.$boxH.find('> div').css('width', '')
        that.$boxB.find('> div').css('width', '')
        that.$boxF && that.$boxF.find('> div').css('width', '')

        setTimeout(function () {
            var bw = that.$boxB.find('> div').width()

            that.$boxB.find('> div').width(bw)
            that.$boxH.find('> div').width(bw)
            that.$boxF && that.$boxF.find('> div').width(bw)
        }, 50)

        if (!that.col_lock_count) that.$lockBox.hide()
        else that.$lockBox.show()

        // colspan for child tr && nodata tr
        that.$tbody.find('> .' + that.classnames.tr_child + ', .datagrid-nodata').each(function () {
            $(this).find('> td').attr('colspan', that.columnModel.length - that.col_lock_count)
        })

        //if (width > 1) width = width - 1
        that.$boxH.css('margin-left', width)
        that.$boxB.css('margin-left', width)
        if (hasFoot) that.$boxF.css('margin-left', width)

        // fixed height
        that.$lockB.height(that.$boxB[0].clientHeight)
        that.$lockB.prop('scrollTop', that.$boxB[0].scrollTop)

        var lockH = that.$lockTableH.height(), H = that.$thead.height(), lockFH = that.$lockTableF && that.$lockTableF.height(), HF = that.$tableF && that.$tableF.height()

        if (lockH != H) {
            if (lockH < H) that.$lockTableH.height(H)
            else that.$tableH.height(lockH)
        }

        if (lockFH && HF && (lockFH != HF)) {
            if (lockFH < HF) that.$lockTableF.find('> thead > tr:first-child > th:visible:first').height(HF)
            else that.$tableF.find('> thead > tr:first-child > th:visible:first').height(lockFH)
        }

        $lockTrs = that.$lockTbody.find('> tr')
        $trs = that.$tbody.find('> tr')

        setTimeout(function () {
            var lockBH = that.$lockTableB.height(), HB = that.$tableB.height()

            if (lockBH != HB) {
                if (lockBH > HB) {
                    $lockTrs.each(function (tr_index) {
                        var $lockTr = $(this), $lockTd = $lockTr.find('> td:visible:first'), newH = $lockTd.outerHeight()

                        if (newH > 30) {
                            $lockTr.height(newH)
                            $trs.eq(tr_index).height(newH)
                        }
                    })
                } else {
                    $trs.each(function (tr_index) {
                        var $tr = $(this), $td = $tr.find('> td:visible:first'), newH = $td.outerHeight()

                        if (newH > 30) {
                            $tr.height(newH)
                            $lockTrs.eq(tr_index).height(newH)
                        }
                    })
                }
            }
        }, 20)

        that.fixedLockItem(1)

        // remove hidden tds
        $lockTrs.find('> td:hidden').remove()

        // events
        that.initLockEvents($lockTrs)
    }

    // init lockTr Events
    Datagrid.prototype.initLockEvents = function ($locktrs) {
        if (!this.$lockTbody) return

        var that = this, options = that.options

        if (!$locktrs) $locktrs = that.$lockTbody.find('> tr')

        $locktrs
            .on('click.zui.datagrid.tr', function (e) {
                var index = $(this).index(), $td = $(e.target).closest('td')

                that.$tbody.find('> tr:eq(' + index + ')').trigger('click.zui.datagrid.tr')
            })
            .on('zui.datagrid.tr.delete', function (e, index) {
                var $tr = $(this)

                if (that.linenumberColumn && that.linenumberColumn.locked) {
                    $tr.nextAll(':not(.' + that.classnames.tr_child + ')').each(function () {
                        var $td = $(this).find('> td.' + that.classnames.td_linenumber), num = parseInt($td.text(), 10)

                        $td.text(num - 1)
                    })
                }
            })
            .on('mouseenter.zui.datagrid', function (e) {
                var $tr = $(this), index = $tr.index()

                $tr.addClass('datagrid-hover')
                that.$tbody.find('> tr:eq(' + index + ')').addClass('datagrid-hover')
            })
            .on('mouseleave.zui.datagrid', function (e) {
                var $tr = $(this), index = $tr.index()

                $tr.removeClass('datagrid-hover')
                that.$tbody.find('> tr:eq(' + index + ')').removeClass('datagrid-hover')
            })

        //contextmenu
        if (options.contextMenuB) {
            $locktrs.each(function () {
                that.tools.contextmenuB($(this), true)
            })
        }
    }

    //api - showhide linenumber column
    Datagrid.prototype.showLinenumber = function (flag) {
        var that = this, options = that.options, model = that.columnModel, numberModel, modelOrder = -1, data = that.data, numberModel_index = $.inArray(that.linenumberColumn, model)

        if (numberModel_index != -1)
            numberModel = model[numberModel_index]

        if (numberModel) {
            if (typeof flag === 'string' && (flag === 'lock' || flag === 'unlock')) {
                that.colLock(numberModel.th, flag === 'lock' ? true : false)
            } else {
                that.showhideColumn(numberModel.th, flag ? true : false)
            }
        } else if (flag) {
            modelOrder = $.inArray(that.childColumn, model)

            modelOrder++
            numberModel = that.linenumberColumn
            numberModel.index = modelOrder
            model.splice(modelOrder, 0, numberModel)

            if (that.inputs && that.inputs.length)
                that.inputs = that.inputs.splice(modelOrder, 0, '')

            var $trsH = that.$thead.find('> tr'), col = '<col style="width:30px;">', $th, rowspan = $trsH.length

            $th = $('<th align="center"' + (rowspan > 1 ? ' rowspan="' + rowspan + ' "' : '') + 'class="' + that.classnames.td_linenumber + (rowspan == 1 ? ' single-row' : '') + '"><div><div class="datagrid-space"></div><div class="datagrid-label">' + that.linenumberColumn.label + '</div></div></th>')

            that.$colgroupH.find('> col:eq(' + modelOrder + ')').before(col)
            that.$colgroupB.find('> col:eq(' + modelOrder + ')').before(col)
            that.$colgroupF && that.$colgroupF.find('> col:eq(' + modelOrder + ')').before(col)
            $trsH.first().find('> th:eq(' + modelOrder + ')').before($th)
            that.$tableF && that.$tableF.find('> thead > tr > th:eq(' + modelOrder + ')').before('<th></th>')

            numberModel.th = $th
            numberModel.width = 30

            that.$tbody.find('> tr').each(function (i) {
                var $tr = $(this), colspan = that.columnModel.length, linenumber = i, paging = that.paging

                if ($tr.hasClass(that.classnames.tr_child) || $tr.hasClass('datagrid-nodata'))
                    $tr.find('> td').attr('colspan', colspan)
                else {
                    linenumber = that.tools.getNoChildDataIndex(linenumber)
                    if (options.linenumberAll)
                        linenumber = ((paging.pageCurrent - 1) * paging.pageSize + (linenumber))

                    $tr.find('> td:eq(' + modelOrder + ')').before('<td align="center" class="' + that.classnames.td_linenumber + '">' + (linenumber + 1) + '</td>')
                }
            })

            that.$tableF && that.$tableF.find('> thead > tr > th:eq(' + modelOrder + ')').before('<th></th>')

            $.each(model, function (i, n) {
                n.index = i
                if (n.th) n.th.data('index', i)
            })

            $th.find('> div').height($th.outerHeight())

            if (flag === 'lock') {
                that.colLock($th, true)
            }
            if (that.$showhide) {
                that.$showhide.remove()
                that.colShowhide(options.columnShowhide)
            }
        }
    }

    //api - showhide checkbox column
    Datagrid.prototype.showCheckboxcol = function (flag) {
        var that = this, options = that.options, model = that.columnModel, numberModel = model[0], checkModel, modelOrder = -1, checkModel_index = $.inArray(that.checkboxColumn, model)

        if (checkModel_index != -1)
            checkModel = that.columnModel[checkModel_index]

        if (checkModel) {
            if (typeof flag === 'string' && (flag === 'lock' || flag === 'unlock')) {
                that.colLock(checkModel.th, flag === 'lock' ? true : false)
            } else {
                that.showhideColumn(checkModel.th, flag)
            }
        } else if (flag) {
            modelOrder = $.inArray(that.linenumberColumn, model)
            if (modelOrder == -1)
                modelOrder = $.inArray(that.childColumn, model)

            modelOrder++
            checkModel = that.checkboxColumn
            checkModel.index = modelOrder
            model.splice(modelOrder, 0, checkModel)

            if (that.inputs && that.inputs.length)
                that.inputs = that.inputs.splice(modelOrder, 0, '')

            var $trsH = that.$thead.find('> tr'), col = '<col style="width:30px;">', $th, $td, rowspan = $trsH.length

            $th = $('<th align="center"' + (rowspan > 1 ? ' rowspan="' + rowspan + ' "' : '') + 'class="' + that.classnames.td_checkbox + (rowspan == 1 ? ' single-row' : '') + '"><div><div class="datagrid-space"></div><div class="datagrid-label"><input type="checkbox" data-toggle="icheck"></div></th>')

            that.$colgroupH.find('> col:eq(' + modelOrder + ')').before(col)
            that.$colgroupB.find('> col:eq(' + modelOrder + ')').before(col)
            that.$colgroupF && that.$colgroupF.find('> col:eq(' + modelOrder + ')').before(col)
            $trsH.first().find('> th:eq(' + modelOrder + ')').before($th)
            that.$tableF && that.$tableF.find('> thead > tr > th:eq(' + modelOrder + ')').before('<th></th>')
            $th.initui()

            checkModel.th = $th
            checkModel.width = 30

            that.$tbody.find('> tr').each(function (i) {
                var $tr = $(this), colspan = that.columnModel.length

                if ($tr.hasClass(that.classnames.tr_child) || $tr.hasClass('datagrid-nodata'))
                    $tr.find('> td').attr('colspan', colspan)
                else {
                    $td = $('<td align="center" class="' + that.classnames.td_checkbox + '"><input type="checkbox" data-toggle="icheck" name="datagrid.checkbox"></td>')
                    $tr.find('> td:eq(' + modelOrder + ')').before($td)
                    $td.initui()
                }
            })

            $.each(model, function (i, n) {
                n.index = i
                if (n.th) n.th.data('index', i)
            })

            $th.find('> div').height($th.outerHeight())

            if (flag === 'lock') {
                that.colLock($th, true)
            }
            if (that.$showhide) {
                that.$showhide.remove()
                that.colShowhide(options.columnShowhide)
            }
        }
    }

    //api - showhide checkbox column
    Datagrid.prototype.showEditCol = function (flag) {
        var that = this, options = that.options, model = that.columnModel, editModel = model[model.length - 1], data = that.data
        if(!options.editEnabled) {
            return false;
        }
        if (editModel === that.editBtnsColumn) {
            that.showhideColumn(editModel.th, flag ? true : false)
        } else if (flag) {
            var $trsH = that.$thead.find('> tr'), col = '<col style="width:110px;">', $th, $td, rowspan = $trsH.length

            editModel = that.editBtnsColumn
            model.push(editModel)
            if(that.$colgroupH)
            that.$colgroupH.append(col)
            if(that.$colgroupB)
            that.$colgroupB.append(col)
            that.$colgroupF && that.$colgroupF.append(col)
            $th = $('<th align="center" rowspan="' + rowspan + '">' + that.editBtnsColumn.label + '</th>')
            $trsH.first().append($th)
            $th.initui().data('index', model.length - 1)
            editModel.th = $th
            editModel.width = 110
            editModel.index = model.length - 1

            if(that.$tbody) {
                that.$tbody.find('> tr').each(function (i) {
                    var $tr = $(this), colspan = that.columnModel.length

                    if ($tr.hasClass(that.classnames.tr_child) || $tr.hasClass('datagrid-nodata'))
                        $tr.find('> td').attr('colspan', colspan)
                    else {
                        $td = $('<td align="center" class="' + that.classnames.s_edit + '">' + $.zui.doRegional(FRAG.gridEditBtn, that.regional) + '</td>')
                        $tr.append($td)
                        $td.initui()
                    }
                });
                that.showActionButton(that.$tbody.find('> tr'))
            }
            that.$tableF && that.$tableF.find('> thead > tr').append('<th></th>')

            if (that.$showhide) {
                that.$showhide.remove()
                that.colShowhide(options.columnShowhide)
            }
        }
    }

    //resize
    Datagrid.prototype.colResize = function () {
        var that = this,
            tools = that.tools,
            columnModel = that.columnModel,
            $thead = that.$thead,
            $resizeMark = that.$grid.find('> .resizeProxy')

        if (!$resizeMark.length) {
            $resizeMark = $('<div class="resizeProxy" style="left:0; display:none;"></div>')
            $resizeMark.appendTo(that.$grid)
        }

        $thead.find('> tr > th').each(function (i) {
            var $th = $(this), $resize = $th.find('> div > .' + that.classnames.th_cell + '> .' + that.classnames.th_resizemark)

            $resize.on('mousedown.zui.datagrid.resize', function (e) {
                var ofLeft = that.$boxH.scrollLeft(), marginLeft = parseInt(that.$boxH.css('marginLeft') || 0, 10), left, index = $th.data('index'), model = columnModel[index], width = model.th.width()
                    , $trs = that.$tbody.find('> tr'), $lockTrs = that.$lockTbody && that.$lockTbody.find('> tr'), lockH = that.$lockTableB && that.$lockTableB.height(), H = that.$tableB.height(), lockH_new, H_new

                if (isNaN(marginLeft)) marginLeft = 0;
                left = tools.getRight($th) - ofLeft + marginLeft;
                //修正一下内部scroll造成的错位
                left += that.$boxH.find('table').offset().left - that.$boxH.offset().left;
                if (that.$lockBox) {
                    left -= that.$lockBox.width();
                }

                that.isResize = true

                if (model.locked) {
                    left = tools.getRight4Lock(model.lockIndex)
                    if (model.lockWidth) width = model.lockWidth
                }

                $resizeMark
                    .show()
                    .css({
                        left: left,
                        bottom: (that.$boxP ? that.$boxP.outerHeight() : 0),
                        cursor: 'col-resize'
                    })
                    .basedrag({
                        scop: true, cellMinW: 20, relObj: $resizeMark,
                        move: 'horizontal',
                        event: e,
                        stop: function () {
                            var new_left = $resizeMark.position().left,
                                move = new_left - left,
                                newWidth = move + width,
                                tableW = that.$tableH.width() + move,
                                lockW = that.$lockTableH && that.$lockTableH.width() + move

                            if (newWidth < 5) newWidth = 20
                            if (model.minWidth && newWidth < model.minWidth) newWidth = model.minWidth
                            if (newWidth != width + move) {
                                tableW = tableW - move + (newWidth - width)
                                lockW = lockW - move + (newWidth - width)
                            }

                            model.width = newWidth

                            if (model.locked) {
                                if (lockW < (that.$grid.width() * 0.75)) {
                                    model.lockWidth = newWidth
                                    that.$lockColgroupH.find('> col:eq(' + index + ')').width(newWidth)
                                    that.$lockColgroupB.find('> col:eq(' + index + ')').width(newWidth)
                                    that.$lockColgroupF && that.$lockColgroupF.find('> col:eq(' + index + ')').width(newWidth)

                                    that.$lockTableH.width(lockW)
                                    that.$lockTableB.width(lockW)
                                    that.$lockTableF && that.$lockTableF.width(lockW)

                                    var margin = that.$lockBox.width()

                                    that.$boxH.css('margin-left', margin - 1)
                                    that.$boxB.css('margin-left', margin - 1)
                                    that.$boxH.find('> div').width(that.$boxH.width())
                                    that.$boxB.find('> div').width(that.$boxH.width())
                                    that.$boxF && that.$boxF.css('margin-left', margin - 1)

                                    that.$colgroupH.find('> col:eq(' + index + ')').width(newWidth)
                                    that.$colgroupB.find('> col:eq(' + index + ')').width(newWidth)
                                    that.$colgroupF && that.$colgroupF.find('> col:eq(' + index + ')').width(newWidth)
                                }
                            } else {
                                setTimeout(function () {
                                    that.$colgroupH.find('> col:eq(' + index + ')').width(newWidth)
                                    that.$colgroupB.find('> col:eq(' + index + ')').width(newWidth)
                                    that.$colgroupF && that.$colgroupF.find('> col:eq(' + index + ')').width(newWidth)
                                }, 1) //setTimeout for chrome
                            }

                            /* fixed height */
                            setTimeout(function () {
                                $trs.css('height', '')
                                H_new = that.$tableB.height()

                                if (that.$lockTbody) {
                                    $lockTrs.css('height', '')
                                    lockH_new = that.$lockTableB.height()
                                    if (lockH_new != lockH || H_new != H) {
                                        if (lockH_new > H_new) {
                                            $lockTrs.each(function (tr_index) {
                                                var $lockTr = $(this), $lockTd = $lockTr.find('> td:eq(' + model.lockIndex + ')'), newH = $lockTd.outerHeight()

                                                if (newH > 30) {
                                                    $lockTr.height(newH)
                                                    $trs.eq(tr_index).height(newH)
                                                }
                                            })
                                        } else {
                                            $trs.each(function (tr_index) {
                                                var $tr = $(this), $td = $tr.find('> td:eq(' + index + ')'), newH = $td.outerHeight()

                                                if (newH > 30) {
                                                    $tr.height(newH)
                                                    $lockTrs.eq(tr_index).height(newH)
                                                }
                                            })
                                        }
                                    }

                                    that.$lockB.height(that.$boxB[0].clientHeight)
                                }

                                if (model.calc) that.$tfoot && that.$tfoot.trigger('zui.datagrid.tfoot.resizeH')

                                that.isResize = false
                            }, 10)

                            $resizeMark.hide()
                            that.resizeFlag = true
                        }
                    })
            })
        })
    }

    //column - add menu button
    Datagrid.prototype.colMenu = function () {
        var that = this, options = that.options, tools = that.tools, regional = that.regional, $ths = that.$trsH.find('> th.' + that.classnames.th_menu), $menu = that.$grid.find('> .' + that.classnames.s_menu)

        if (!$menu.legnth) {
            $menu = $($.zui.doRegional(FRAG.gridMenu, regional))
            $menu.hide().appendTo(that.$grid)
            that.$menu = $menu
        }
        that.colShowhide(options.columnShowhide)

        $ths.each(function () {
            var $th = $(this),
                index = $th.data('index'),
                model = that.columnModel[index],
                $cell = $th.find('> div > .' + that.classnames.th_cell),
                $btnBox = $cell.find('> .' + that.classnames.btn_menu),
                $btn

            if (!$btnBox.length) {
                $btn = $('<button type="button" class="btn btn-default"><i class="icon icon-bars"></button>'),
                    $btnBox = $('<div class="' + that.classnames.btn_menu + '"></div>').append($btn)

                $btnBox.appendTo($cell)

                $btn.click(function () {
                    var $tr = $th.closest('tr'), rowspan = parseInt(($th.attr('rowspan') || 1), 10), left = $(this).offset().left - that.$grid.offset().left - 1, top = (that.$trsH.length - rowspan) * 33 + (13 * rowspan) + 11, $showhide = $menu.find('> ul > li.datagrid-li-showhide'), menu_width, submenu_width
                    var $otherBtn = $menu.data('zui.datagrid.menu.btn')

                    if ($otherBtn && $otherBtn.length) $otherBtn.removeClass('active')
                    $(this).addClass('active')
                    if ($showhide.length && that.$showhide) {
                        that.$showhide.appendTo($showhide)
                        submenu_width = that.$showhide.data('width')
                    }
                    $menu
                        .css({'position': 'absolute', 'top': top, left: left})
                        .show()
                        .data('zui.datagrid.menu.btn', $btn)
                        .siblings('.' + that.classnames.s_menu).hide()

                    menu_width = $menu.outerWidth()

                    var position = function (left) {
                        if (that.$boxH.width() - left < menu_width) {
                            $menu.css({left: left - menu_width + 18}).addClass('position-right')
                        } else {
                            $menu.css({left: left}).removeClass('position-right')
                        }
                    }
                    var fixedLeft = function ($btn) {
                        that.$boxB.scroll(function () {
                            var left = $btn.offset().left - that.$grid.offset().left - 1

                            position(left)
                        })
                    }

                    position(left)
                    fixedLeft($btn)

                    tools.locking($th)

                    // quick sort
                    var $asc = $menu.find('> ul > li.' + that.classnames.li_asc),
                        $desc = $asc.next(), qs = false, sa = false

                    if(model.hasOwnProperty('quicksort')) {
                        qs = model.quicksort;
                        if(qs) {
                            if(model.hasOwnProperty('sortAsc')) {
                                sa = model.sortAsc;
                                $asc.toggleClass('sort-active', sa)
                                $desc.toggleClass('sort-active', !sa)
                            }
                        }
                    }

                    $asc.click(function () {
                        model.sortAsc = false
                        tools.quickSort(model)
                    })

                    $desc.click(function () {
                        model.sortAsc = true
                        tools.quickSort(model)
                    })
                })
            }
        })
    }

    // show or hide columns on btn menu
    Datagrid.prototype.colShowhide = function (showFlag) {
        var that = this, options = that.options, tools = that.tools, $menu = that.$menu, $ul = $menu.find('> ul'), $showhideli = $ul.find('> li.' + that.classnames.li_showhide)

        if (showFlag) {
            if (!that.$showhide) {
                tools.createShowhide()
                tools.showSubMenu($showhideli, $menu, that.$showhide)
            }
        } else {
            $showhideli.hide()
        }
    }

    // api - show or hide column
    Datagrid.prototype.showhideColumn = function (column, showFlag) {
        var that = this, tools = that.tools, index, model

        if ($.type(column) === 'number') {
            index = parseInt(column, 10)
            if (index < 0) return
        } else {
            index = column.data('index')
        }

        model = that.columnModel[index]

        if (model) {
            if (model.locked) {
                if (showFlag) return
                else that.colLock(model.th, showFlag)
            }
            tools.showhide(model, showFlag)
        }
    }

    // paging
    Datagrid.prototype.initPaging = function () {
        var that = this, tools = that.tools, options = that.options, paging = that.paging, pr = $.zui.regional.pagination,
            btnpaging = FRAG.gridPaging, pageNums = [], pageCount = paging.pageCount, interval,
            selectPages = [], pagingHtml = $.zui.StrBuilder()
        if(!options.paging && !options.checkAction){
            return;
        }
        if(options.paging){
            interval = tools.getPageInterval(pageCount, paging.pageCurrent, paging.showPagenum)

            for (var i = interval.start; i < interval.end; i++) {
                pageNums.push(FRAG.gridPageNum.replace('#num#', i).replace('#active#', (paging.pageCurrent == i ? ' active' : '')))
            }

            btnpaging = $.zui.doRegional(btnpaging.replaceAll('#pageCurrent#', paging.pageCurrent).replaceAll('#count#', paging.total + '/' + parseInt((pageCount || 0), 10)), pr)

            pagingHtml
                .add('<div class="paging-content" style="width:' + that.$boxB.width() + 'px;">')
                .add('<div class="datagrid-paging">')
                .add(btnpaging.replace('#pageNumFrag#', pageNums.join('')))
                .add('</div>')
                .add('</div>')
        } else {
            pagingHtml
                .add('<div class="paging-content" style="height: 36px; width:' + that.$boxB.width() + 'px;">')
                .add('</div>')
        }

        that.$boxP.html(pagingHtml.toString())
        that.$boxP.initui()
        if(options.checkAction){
            that.$boxP.find('div.paging-content').prepend($('<div>').addClass('datagrid-checkaction').addClass('btn-group').addClass('pull-left').html(options.checkAction));
            that.$boxP.find('div.paging-content').find('div.datagrid-paging').addClass('pull-right');
        }
        if(!options.paging){
            return;
        }
        //events
        var $select = that.$boxP.find('select'),
            $pagenum = that.$boxP.find('ul.pager'),
            $pagetotal = $pagenum.find('> li.page-total'),
            $first = $pagenum.find('> li.page-first'),
            $prev = $first.next(),
            $next = $prev.nextAll('.page-next'),
            $last = $next.next()

        var disablePrev = function () {
            $first.addClass('disabled')
            $prev.addClass('disabled')
        }
        var enablePrev = function () {
            $first.removeClass('disabled')
            $prev.removeClass('disabled')
        }
        var disableNext = function () {
            $next.addClass('disabled')
            $last.addClass('disabled')
        }
        var enableNext = function () {
            $next.removeClass('disabled')
            $last.removeClass('disabled')
        }
        var pageFirst = function () {
            disablePrev()
            enableNext()
        }
        var pageLast = function () {
            enablePrev()
            disableNext()
        }
        var setPageSize = function(pageSize) {
            $select.empty()

            if (!pageSize) pageSize = that.paging.pageSize

            selectPages = paging.selectPageSize.split(',')
            selectPages.push(String(pageSize))

            $.unique(selectPages).sort(function(a, b) { return a - b })

            var opts = []

            $.each(selectPages, function(i, n) {
                opts.push('<option value="'+ n +'"'+ (n == paging.pageSize && 'selected') +'>'+ n +'/'+ pr.page +'</option>')
            })

            $select.html(opts.join('')).selectpicker('refresh')
        }

        if (paging.pageCurrent == 1) pageFirst()
        if (paging.pageCurrent == paging.pageCount) {
            pageLast()
            if (paging.pageCurrent == 1) disablePrev()
        }
        if (!paging.total) disableNext()
        setPageSize()

        that.$boxP.on('click.datagrid.pagenum', 'li.page-num', function (e) {
            var $num = $(this)
            if (!$num.hasClass('active')) {
                that.jumpPage($num.text())
            }
            e.preventDefault()
        }).one('zui.datagrid.paging.pageSize', function(e, pageSize) {
            setPageSize(pageSize)
        }).one('change', 'ul.pager > select', function() {
            var pageSize = $(this).val()
            that.jumpPage(null, pageSize)
        })

        $first.on('click', function () {
            if (that.paging.pageCurrent > 1)
                that.jumpPage(1)
        })

        $prev.on('click', function () {
            if (that.paging.pageCurrent > 1)
                that.jumpPage(that.paging.pageCurrent - 1)
        })

        $next.on('click', function () {
            if (that.paging.pageCurrent < that.paging.pageCount)
                that.jumpPage(that.paging.pageCurrent + 1)
        })

        $last.on('click', function () {
            if (that.paging.pageCurrent < that.paging.pageCount)
                that.jumpPage(that.paging.pageCount)
        })
    }

    /**
     * 数据导出
     * @returns {undefined}
     */
    Datagrid.prototype.export = function(postData) {
        var that = this, options=this.options;
        var opts = options.exportOption;
        if(typeof(postData)=='string' && postData=='init'){
            postData = {};
            postData[$.zui.pageInfo.pageVar] = 1
        } else {
            //postData.getData[$.zui.pageInfo.pageVar] = postData[$.zui.pageInfo.pageVar];
        }
        if (typeof opts === 'string') {
            opts = opts.toObj();
        }
        if(options.sortMode) {
            postData[$.zui.pageInfo.orderField] = options.sortMode.name;
            postData[$.zui.pageInfo.orderDirection] = options.sortMode.direction;
        }
        postData[$.zui.pageInfo.pageSize] = 500;//that.paging.pageSize

        if (options.postData) {
            if (typeof options.postData === 'string') {
                if (options.postData.trim().startsWith('{')) options.postData = options.postData.toObj()
                else options.postData = options.postData.toFunc()
            }
            var _postData = options.postData;
            if (typeof options.postData === 'function') {
                _postData = options.postData.apply()
            } else if (typeof options.postData === 'object') {
                _postData = options.postData;
            }
            if(_postData) {
                postData = $.extend(postData, _postData);
            }
        }
        if(postData.hasOwnProperty('getData')) {
            opts.options.data.postData = postData.postData;
            opts.options.data.getData = postData.getData;
        } else {
            opts.options.data.postData = postData;
        }
        opts.options.success=function(json){
            if(json.status==1) {
                if(json.info) {
                    Do.ready('tips', function(){
                        toastr.info(json.info);
                    });
                }
                json.data.page++;
                json.data.postData[$.zui.pageInfo.pageVar] = json.data.page;
                if(json.data.page<=json.data.pagecount) {
                    //还有下一页
                    that.export(json.data);
                } else {
                    json.url = opts.options.url;
                    Do.ready('download', function(){
                        json.target = $('body');//that.$boxB;
                        json.data.download = true;
                        $.zui.ajax('ajaxdownload', json);
                    });
                }
            }
        }
        $.ajax(opts.options);
//        if (opts.options && opts.options.url) {
//            if (opts.type === 'dialog') {
//                $.zui.dialog(opts.options);
//            } else if (opts.type === 'navtab') {
//                $.zui.navtab(opts.options);
//            } else if (opts.type === 'file') {
//                opts.options.target = that.$boxB;
//                $.zui.ajax('ajaxdownload', opts.options);
//            } else {
//                $.zui.ajax('doajax', opts.options);
//            }
//        }
    }

    Datagrid.prototype.jumpPage = function (pageCurrent, pageSize) {
        var that = this, paging = that.paging, pageCount = paging.pageCount

        if (pageCurrent && isNaN(pageCurrent)) return
        if (pageSize && isNaN(pageSize))       return
        if (pageCurrent) {
            pageCurrent = parseInt(pageCurrent, 10)

            if (pageCurrent < 1)         pageCurrent = 1
            if (pageCurrent > pageCount) pageCurrent = pageCount
            if (pageCurrent == paging.pageCurrent) return
        }
        if (pageSize) {
            pageSize = parseInt(pageSize, 10)

            if (that.options.dataFrom != 'remote') {
                if (paging.pageSize > paging.total) return
            }
        }

        that.tools.jumpPage(pageCurrent, pageSize)
    }

    // api - add
    Datagrid.prototype.doAddRow = function () {
        if (!this.tools.beforeEdit())
            return

        if (!this.options.editUrl) {
            $.zui.debug('Datagrid Plugin: Edit url is not set!')
            return
        }

        this.dialogEdit({}, true);
    }

    // 初始化行内编辑按钮
    Datagrid.prototype.showActionButton = function ($trs) {
        var that = this, options = that.options, tools = that.tools, columnModel = that.columnModel, $editTd

        if (that.options.editUrl)
            that.options.editUrl = that.options.editUrl.replace(/{\/?[^}]*}/g, '')

        if (!$trs) $trs = that.$tbody.find('> tr')

        $editTd = $trs.find('> td.' + that.classnames.s_edit)

        /* events */
        $editTd.each(function () {
            var $td = $(this), $btns = $td.find('button'), $edit = $btns.first(), $delete = $edit.next()

            $edit.on('click', function (e, data) {
                var $btn = $(this), $tr = $btn.closest('tr')
                that.doEditRow($tr, false);
                e.stopPropagation()
            })

            $delete.on('click', function (e) {
                var $btn = $(this), $tr = $btn.closest('tr')
                that.delRows($tr)
                e.stopPropagation()
            })
        })
    }

    // Api - changeStatus tr
    Datagrid.prototype.changeRowsStatus = function (url, rows) {
        var that = this, options = that.options, $trs
        if (!url) {
            $.zui.alertmsg('error', '暂时不支持修改状态');
            return;
        }

        if (typeof rows === 'object') {
            $trs = rows
        } else if (isNaN(rows)) {
            var $myTrs = that.$tbody.find('> tr:not(.' + that.classnames.tr_child + ')'), $delTrs, rows = rows.split(',')

            rows = rows.unique()
            rows.sort(function(a, b) {return a.trim() - b.trim()})

            $.each(rows, function (i, n) {
                var tr = $myTrs.eq(parseInt(n.trim(), 10))

                if (tr && tr.length) {
                    if (!$delTrs) $delTrs = tr
                    else $delTrs = $delTrs.add(tr)
                }
            })

            $trs = $delTrs
        } else if (!isNaN(rows)) {
            $trs = this.$tbody.find('> tr:not(.' + that.classnames.tr_child + ')').eq(rows)
        }

        if (!$trs || !$trs.length)
            return

        var postData = []

        $trs.each(function () {
            var $tr = $(this), index = $tr.index(), data, delData

            if (that.isDom)
                data = $tr.data('initData') || that.tools.setDomData($tr)
            else
                data = that.data[that.tools.getNoChildDataIndex(index)]

            if (options.dataPK) {
                postData.push(data[options.dataPK])
            } else {
                if (options.jsonPrefix) {
                    delData = {}
                    $.each(data, function (name, value) {
                        if (name === 'gridCheckbox' || name === 'gridEdit')
                            return true
                        delData[options.jsonPrefix + '.' + name] = value
                    })
                } else {
                    delData = $.extend({}, data)
                    delete delData.gridCheckbox
                    delete delData.gridEdit
                }

                postData.push(delData)
            }
        })

        var type = options.delType, opts = {url: url, data: (options.dataPK ? [{name: options.dataPK, value: postData.join(',')}] : JSON.stringify(postData)), type: 'POST', okCallback: function (json) {
                that.refresh();
            }}

        if (type && type === 'raw' && !options.dataPK) {
            opts.contentType = 'application/json'
        } else {
            !options.dataPK && (opts.data = {json: opts.data})
            type && type !== 'raw' && (opts.type = type)
        }

        $.zui.ajax('doajax', opts)
    }

    // Api - edit
    Datagrid.prototype.doEditRow = function (rows, isAdd) {

        var that = this, $editBtn, datas = []

        if (!rows.length)
            return
        rows.each(function () {
            var $tr = $(this), data_index = $tr.index(), data

            data_index = that.tools.getNoChildDataIndex(data_index)

            if (that.isDom)
                data = $tr.data('initData') || tools.setDomData($tr)
            else
                data = that.data[data_index]

            datas.push(data)
        })

        if (!that.tools.beforeEdit(rows, datas)) {
            return
        }

        if (!that.options.editUrl) {
            $.zui.debug('Datagrid Plugin: Edit url is not set!')
            return
        }

        rows.each(function (i) {
            var $tr = $(this)

            $editBtn = $tr.find('> td.' + that.classnames.s_edit + ' button.edit')
            that.dialogEdit($tr, isAdd, datas[i])
            return false
        })
    }

    // Api - del tr
    Datagrid.prototype.delRows = function (rows) {
        var that = this, options = that.options, beforeDelete = options.beforeDelete, confirmMsg = options.delConfirm, $trs
        if (options.dataFrom == 'remote' && !options.delUrl) {
            $.zui.alertmsg('error', '暂时不支持数据删除操作');
            return;
        }

        if (beforeDelete) {
            if (typeof beforeDelete === 'string')
                beforeDelete = beforeDelete.toFunc()
            if (typeof beforeDelete === 'function') {
                if (!beforeDelete.call(this)) {
                    return
                }
            }
        }

        if (typeof rows === 'object') {
            $trs = rows
        } else if (isNaN(rows)) {
            var $myTrs = that.$tbody.find('> tr:not(.' + that.classnames.tr_child + ')'), $delTrs, rows = rows.split(',')

            rows = rows.unique()
            rows.sort(function(a, b) {return a.trim() - b.trim()})

            $.each(rows, function (i, n) {
                var tr = $myTrs.eq(parseInt(n.trim(), 10))

                if (tr && tr.length) {
                    if (!$delTrs) $delTrs = tr
                    else $delTrs = $delTrs.add(tr)
                }
            })

            $trs = $delTrs
        } else if (!isNaN(rows)) {
            $trs = this.$tbody.find('> tr:not(.' + that.classnames.tr_child + ')').eq(rows)
        }

        if (!$trs || !$trs.length)
            return

        var doDel = function () {
            // remote delete
            if (options.delUrl) {
                var postData = [], callback = options.delCallback

                $trs.each(function () {
                    var $tr = $(this), index = $tr.index(), data, delData

                    if (that.isDom)
                        data = $tr.data('initData') || that.tools.setDomData($tr)
                    else
                        data = that.data[that.tools.getNoChildDataIndex(index)]

                    if (options.dataPK) {
                        postData.push(data[options.dataPK])
                    } else {
                        if (options.jsonPrefix) {
                            delData = {}
                            $.each(data, function (name, value) {
                                if (name === 'gridCheckbox' || name === 'gridEdit')
                                    return true
                                delData[options.jsonPrefix + '.' + name] = value
                            })
                        } else {
                            delData = $.extend({}, data)
                            delete delData.gridCheckbox
                            delete delData.gridEdit
                        }

                        postData.push(delData)
                    }
                })

                if (typeof callback === 'string')
                    callback = callback.toFunc()

                var type = options.delType, opts = {url: options.delUrl, data: (options.dataPK ? [{name: options.dataPK, value: postData.join(',')}] : JSON.stringify(postData)), type: 'POST', okCallback: callback || function (json) {
                        that.refresh();
                    }}

                if (type && type === 'raw' && !options.dataPK) {
                    opts.contentType = 'application/json'
                } else {
                    !options.dataPK && (opts.data = {json: opts.data})
                    type && type !== 'raw' && (opts.type = type)
                }

                $.zui.ajax('doajax', opts)
            } else { // local delete
                that.refresh();
            }
        }

        if (confirmMsg) {
            if (typeof confirmMsg !== 'string') confirmMsg = $trs.length == 1 ? $.zui.getRegional('datagrid.delMsg') : $.zui.getRegional('datagrid.delMsgM')

            that.$grid.alertmsg('confirm', confirmMsg, {
                okCall: function () {
                    doDel()
                }
            })
        } else {
            doDel()
        }
    }

    // edit
    Datagrid.prototype.dialogEdit = function ($tr, isAdd, data) {
        if (!this.tools.beforeEdit($tr, data)) {
            return false
        }

        var that = this, options = that.options, tools = that.tools, columnModel = that.columnModel,
        $dialog, $form, html = '', title

        if (!data && !isAdd) {
            var data_index = $tr.index()

            data_index = tools.getNoChildDataIndex(data_index)

            if (that.isDom) data = $tr.data('initData') || tools.setDomData($tr)
            else data = that.data[data_index]
        }

        title = options.gridTitle || 'datagrid'

        if (isAdd) {
            title += ' - ' + $.zui.getRegional('datagrid.add')
        } else {
            title += ' - ' + $.zui.getRegional('datagrid.edit')
        }

        var opts = {
            id: zhpConfig.Controller+'_'+zhpConfig.Action+(isAdd?'Add':'Edit'),
            mask: true,
            title: title,
            url: options.editUrl,
            data: data,
            onLoad: function($this) {
                $this.find('form').attr('data-callback', options.editCallback).initui();
            },
            onClose: function($this){
                $('body').css({overflow:'hidden'});
            }
        };
        if(options.dataPK && data) {    //如果指定了主键，就不需要把整行的数据给扔过去了
            opts.data = {};
            opts.data[options.dataPK] = data[options.dataPK];
            opts.id += '_'+data[options.dataPK];
        }
        if(options.editMode!='dialog') {
            opts.external = true;
            that.$grid.navtab(opts);
        } else {
            that.$grid.dialog(opts);
        }
    }

    /* resize */
    Datagrid.prototype.resizeGrid = function () {
        var that = this, $target = that.$grid.getPageTarget(), parentW, parentH
        var _resizeGrid = function () {

            if (that.initFixedW && String(that.options.width).endsWith('%')) {
                parentW = that.$grid.parent().width()

                that.fixedWidth()

                if (that.options.hasChild && that.options.childOptions) {
                    that.$tbody.find('> tr.' + that.classnames.tr_child + ':visible').each(function () {
                        var $child = $(this), $tr = $child.prev(), $table = $tr.data('zui.datagrid.child')

                        if ($table && $table.length) {
                            $table.datagrid('fixedWidth')
                        }
                    })
                }
            }
            that.fixedHeight()
            if (String(that.options.height).endsWith('%')) {
                that.tools.setBoxbH()
            }
        }

        // for tab
        $('a[data-toggle="tab"]').on('shown.bs.tab', $.proxy(function (e) {
            if (!that.$element.data('zui.datagrid.init.tab')) {
                var $target = $(e.target), $box = $target.data('target'), href = $target.attr('href')

                if (!$box)
                    $box = $(href)

                if ($box && $box.length) {
                    if ($box.find(that.$element).length) {
                        that.$element.data('zui.datagrid.init.tab', true)
                        that.$element.datagrid('fixedHeight')
                    }
                }
            }
        }, that))

        $(window).on($.zui.eventType.resizeGrid, $.proxy(_resizeGrid, that))
    }


    // DATAGRID PLUGIN DEFINITION
    // =======================

    function Plugin(option) {
        var args = arguments,
            property = option

        return this.each(function () {
            var $this = $(this),
                options = $.extend(true, {}, Datagrid.DEFAULTS, typeof option === 'object' && option),
                data = $this.data('zui.datagrid')
            var op = $this.data('options');
            if(typeof(op)==='string') {
                options = $.extend(true, {}, options, op.toObj());
                $this.removeAttr('data-options');
            }

            if (options.checkAction) {
                var actions = [];
                var _tmp = options.checkAction.split(',');
                $.each(_tmp, function () {
                    var _tmp1 = this.split('|'),_name = _tmp1[0],_title = _tmp1[0],_icon = '',_cls='';
                    if(_tmp1.length > 3 && _tmp1[3]) {
                        _cls = 'btn-'+_tmp1[3]
                    }
                    if (_tmp1.length > 2 && _tmp1[2]) {
                        _icon = '<i class="icon icon-' + _tmp1[2] + '"></i> ';
                    }
                    if (_tmp1.length > 1) {
                        _title = _tmp1[1];
                    }
                    if ($.isExitsFunction(_name)) {
                        actions.push('<button class="btn btn-nm '+_cls+' disabled" onclick="' + _name + '(this)">' + _icon + _title + '</button>');
                    }
                });
                options.checkAction = actions.join('');
            }


            if (!data)
                $this.data('zui.datagrid', (data = new Datagrid(this, options)))
            if (typeof property === 'string' && $.isFunction(data[property])) {
                [].shift.apply(args)
                if (!args)
                    data[property]()
                else
                    data[property].apply(data, args)
            } else {
                data.init()
            }
        })
    }

    var old = $.fn.datagrid

    $.fn.datagrid = Plugin
    //$.fn.datagrid.Constructor = Datagrid
    $.datagrid = Datagrid

    // DATAGRID NO CONFLICT
    // =================
    $.fn.datagrid.noConflict = function () {
        $.fn.datagrid = old
        return this
    }

}(jQuery);
function parseURL(url) {
    var a = document.createElement('a');
    a.href = url;
    return {
        source: url,
        protocol: a.protocol.replace(':',''),
        host: a.hostname,
        port: a.port,
        query: a.search,
        params: (function(){
            var ret = {},
                seg = a.search.replace(/^\?/,'').split('&'),
                len = seg.length, i = 0, s;
            for (;i<len;i++) {
                if (!seg[i]) { continue; }
                s = seg[i].split('=');
                ret[s[0]] = s[1];
            }
            return ret;
        })(),
        file: (a.pathname.match(/\/([^\/?#]+)$/i) || [,''])[1],
        hash: a.hash.replace('#',''),
        path: a.pathname.replace(/^([^\/])/,'/$1'),
        relative: (a.href.match(/tps?:\/\/[^\/]+(.+)/) || [,''])[1],
        segments: a.pathname.replace(/^\//,'').split('/')
    };
}